Author

DaaniiH

Published

June 1, 2025

1 Libraries

Code anzeigen
library(readr)
library(readxl)
library(pxmake)   # Load PX files
library(pxR)      # Load PX files
library(jsonlite) # Load JSON files

library(purrr)

library(writexl)  # Write Excel files

library(dplyr)
library(tidyverse)
library(ggplot2)  # Diagrams
library(ggforce)  # Diagrams
library(DT)       # datatable()

library(DescTools)

2 Initial data load

2.1 Lookup tables

2.1.1 Parteinamen

Code anzeigen
# Dataframe bilden aus zwei Vektoren
parteinamen <- data.frame(
  kuerzel = c("fdp", "sps", "svp", "mitte", "evp", "gps", "glp", "ucsp", "pda", "sd", "edu", "fps", "lega", "kvp", "mcg", "cvp", "bdp", "lps", "ldu", "poch", "rep", "eco", "sgv", "sbv", "sgb", "travs", "sav", "vsa", "vpod", "voev", "tcs", "vcs", "acs", "sbk", "ssv", "gem", "kdk", "kkjpd", "gdk", "ldk"),
  
  name = c("Freisinnig-demokratische Partei (FDP.Die Liberalen)", "Sozialdemokratische Partei", "Schweizerische Volkspartei (bis 1936 Parolen der BGB Bern)", "Die Mitte", "Evangelische Volkspartei", "Grüne Partei der Schweiz", "Grünliberale Partei", "Christlichsoziale Partei der Schweiz (von der CVP unabhängige CSP)", "Partei der Arbeit", "Schweizer Demokraten", "Eidgenössisch-Demokratische Union", "Autopartei", "Lega dei Ticinesi", "Katholische Volkspartei", "Mouvement Citoyens Genevois", "Christlichdemokratische Volkspartei", "Bürgerlich-Demokratische Partei", "Liberale Partei der Schweiz", "Landesring der Unabhängigen", "Progressive Organisationen der Schweiz", "Schweizerische Republikanische Bewegung", "Economiesuisse (bis 15.9.2000: Schweizerischer Handels- und Industrieverein SHIV (Vorort))", "Schweizerischer Gewerbeverband", "Schweizer Bauernverband", "Schweizerischer Gewerkschaftsbund", "Travail.Suisse (bis 2002: Parolen des Christlichnationalen Gewerkschaftsbunds (CNG); dieser fusionierte per 1.1.2003 mit der VSA zu Travail.Suisse)", "Schweizerischer Arbeitgeberverband (bis 1996: Zentralverband Schweizerischer Arbeitgeber-Organisationen ZSAO)", "Vereinigung schweizerischer Angestelltenverbände", "Verband des Personals öffentlicher Dienste", "Verband öffentlicher Verkehr", "Touring Club Schweiz", "Verkehrs-Club der Schweiz", "Automobil Club der Schweiz", "Schweizer Bischofskonferenz", "Schweizerischer Städteverband", "Schweizerischer Gemeindeverband", "Konferenz der Kantonsregierungen", "Konferenz der kantonalen Justiz- und Polizeidirektoren", "Schweizerische Gesundheitsdirektorenkonferenz", "Konferenz der kantonalen Landwirtschaftsdirektoren"),
  stringsAsFactors = FALSE)

# Neue Spalte mit "p-" Präfix für späteren Lookup der Parteiparolen
parteinamen$p_kuerzel <- paste0("p-", parteinamen$kuerzel) 

# Parteikürzel in Grossbuchstaben 
parteinamen$kuerzel <-  toupper(parteinamen$kuerzel)

# Spaltenreihenfolge anpassen: p_kuerzel, kuerzel, name
parteinamen <- parteinamen[, c("p_kuerzel", "kuerzel", "name")]

# Anzeige mit datatable()
datatable(parteinamen,
          class = 'nowrap',
          filter = 'top',
          options = list(pageLength = 7,
                         scrollX = TRUE,
                         search = list(regex = TRUE,
                                       caseInsensitive = TRUE)))

2.2 Abstimmungen

Code anzeigen
# Schweizweite Abstimmungen
voting_raw <- read_delim("~/CAS/Zertifikatsarbeit/data/votes/abstimmungen_swissvotes_DATASET CSV 09-02-2025.csv",
                     delim = ";",
                     escape_double = FALSE,
                     trim_ws = TRUE,
                     show_col_types = FALSE)

# TODO evt. nur random sample berechnen auf grund Grösse...
datatable(voting_raw,
          class = 'nowrap',
          filter = 'top',
          options = list(pageLength = 7,
                         scrollX = TRUE,
                         search = list(regex = TRUE,
                                       caseInsensitive = TRUE)))

2.3 Wahlen

2.3.1 Bundesebene

2.3.1.1 Nationalrat

Code anzeigen
# # PX-Datei einlesen
# elec_nationalrat_px <- read.px("~/CAS/Zertifikatsarbeit/data/elections/px-x-1702020000_103_BUND_NATIONALRAT.px")
# 
# # View PX Format
# str(elec_nationalrat_px)
# 
# # PX-Objekt in Dataframe umwandeln
# elec_nationalrat <- as.data.frame(elec_nationalrat_px)


# Nationalrat: Daten einlesen
elec_nationalrat  <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/Ratsmitglieder_1848_DE_BUND_NR.xlsx")

str(elec_nationalrat)
tibble [492 × 21] (S3: tbl_df/tbl/data.frame)
 $ Active               : logi [1:492] TRUE FALSE FALSE TRUE TRUE FALSE ...
 $ FirstName            : chr [1:492] "Jean-Luc" "Jean-Luc" "Jean-Luc" "Cyril" ...
 $ LastName             : chr [1:492] "Addor" "Addor" "Addor" "Aellen" ...
 $ GenderAsString       : chr [1:492] "m" "m" "m" "m" ...
 $ CantonName           : chr [1:492] "Wallis" "Wallis" "Wallis" "Genf" ...
 $ CantonAbbreviation   : chr [1:492] "VS" "VS" "VS" "GE" ...
 $ CouncilName          : chr [1:492] "Nationalrat" "Nationalrat" "Nationalrat" "Nationalrat" ...
 $ ParlGroupName        : chr [1:492] "Fraktion der Schweizerischen Volkspartei" "Fraktion der Schweizerischen Volkspartei" "Fraktion der Schweizerischen Volkspartei" "FDP-Liberale Fraktion" ...
 $ ParlGroupAbbreviation: chr [1:492] "V" "V" "V" "RL" ...
 $ PartyName            : chr [1:492] "Schweizerische Volkspartei" "Schweizerische Volkspartei" "Schweizerische Volkspartei" "FDP.Die Liberalen" ...
 $ PartyAbbreviation    : chr [1:492] "SVP" "SVP" "SVP" "FDP-Liberale" ...
 $ MaritalStatusText    : chr [1:492] "verheiratet" "verheiratet" "verheiratet" NA ...
 $ Nationality          : chr [1:492] "Schweiz,Italien" "Schweiz,Italien" "Schweiz,Italien" "Schweiz" ...
 $ BirthPlace_City      : chr [1:492] "Lausanne" "Lausanne" "Lausanne" "Genf" ...
 $ BirthPlace_Canton    : chr [1:492] "Waadt" "Waadt" "Waadt" "Genf" ...
 $ Mandates             : chr [1:492] "Vice-président de l'UDC du Valais romand; Membre du Comité central de l'UDC Suisse; Député au Grand Conseil: 20"| __truncated__ "Vice-président de l'UDC du Valais romand; Membre du Comité central de l'UDC Suisse; Député au Grand Conseil: 20"| __truncated__ "Vice-président de l'UDC du Valais romand; Membre du Comité central de l'UDC Suisse; Député au Grand Conseil: 20"| __truncated__ "PLR Genève" ...
 $ DateJoining          : chr [1:492] "04.12.2023" "30.11.2015" "02.12.2019" "04.12.2023" ...
 $ DateLeaving          : chr [1:492] NA "01.12.2019" "03.12.2023" NA ...
 $ Citizenship          : chr [1:492] "Sainte-Croix (VD),Savièse (VS)" "Sainte-Croix (VD),Savièse (VS)" "Sainte-Croix (VD),Savièse (VS)" "Genf (GE)" ...
 $ DateOfBirth          : chr [1:492] "22.04.1964" "22.04.1964" "22.04.1964" "29.02.1972" ...
 $ DateOfDeath          : logi [1:492] NA NA NA NA NA NA ...

2.3.1.2 Ständerat

Code anzeigen
# Ständerat: Daten einlesen
elec_ständerat  <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/Ratsmitglieder_1848_DE_BUND_SR.xlsx")

str(elec_ständerat)
tibble [155 × 21] (S3: tbl_df/tbl/data.frame)
 $ Active               : logi [1:155] TRUE FALSE TRUE FALSE FALSE FALSE ...
 $ FirstName            : chr [1:155] "Marianne" "Marianne" "Pirmin" "Pirmin" ...
 $ LastName             : chr [1:155] "Binder-Keller" "Binder-Keller" "Bischof" "Bischof" ...
 $ GenderAsString       : chr [1:155] "f" "f" "m" "m" ...
 $ CantonName           : chr [1:155] "Aargau" "Aargau" "Solothurn" "Solothurn" ...
 $ CantonAbbreviation   : chr [1:155] "AG" "AG" "SO" "SO" ...
 $ CouncilName          : chr [1:155] "Ständerat" "Nationalrat" "Ständerat" "Nationalrat" ...
 $ ParlGroupName        : chr [1:155] "Die Mitte-Fraktion. Die Mitte. EVP." "Die Mitte-Fraktion. Die Mitte. EVP." "Die Mitte-Fraktion. Die Mitte. EVP." "Fraktion CVP/EVP/glp" ...
 $ ParlGroupAbbreviation: chr [1:155] "M-E" "M-E" "M-E" "M-E" ...
 $ PartyName            : chr [1:155] "Die Mitte" "Christlichdemokratische Volkspartei der Schweiz" "Die Mitte" "Christlichdemokratische Volkspartei der Schweiz" ...
 $ PartyAbbreviation    : chr [1:155] "M-E" "CVP" "M-E" "CVP" ...
 $ MaritalStatusText    : chr [1:155] "verheiratet" "verheiratet" NA NA ...
 $ Nationality          : chr [1:155] "Schweiz" "Schweiz" "Schweiz" "Schweiz" ...
 $ BirthPlace_City      : chr [1:155] "Zürich" "Zürich" "Solothurn" "Solothurn" ...
 $ BirthPlace_Canton    : chr [1:155] "Zürich" "Zürich" "Solothurn" "Solothurn" ...
 $ Mandates             : chr [1:155] "Mitglied Präsidium Die Mitte Schweiz; Präsidentin Die Mitte Aargau" "Mitglied Präsidium Die Mitte Schweiz; Präsidentin Die Mitte Aargau" "Legislative des Kantons (Kantonsrat): von April 2005 bis November 2007; Exekutive der Gemeinde (Gemeinderat) So"| __truncated__ "Legislative des Kantons (Kantonsrat): von April 2005 bis November 2007; Exekutive der Gemeinde (Gemeinderat) So"| __truncated__ ...
 $ DateJoining          : chr [1:155] "04.12.2023" "02.12.2019" "04.12.2023" "03.12.2007" ...
 $ DateLeaving          : chr [1:155] NA "03.12.2023" NA "04.12.2011" ...
 $ Citizenship          : chr [1:155] "Baden (AG),Untersiggenthal (AG),Zurzach (Bad Zurzach) (AG)" "Baden (AG),Untersiggenthal (AG),Zurzach (Bad Zurzach) (AG)" "Eggersriet (SG),Grub (AR)" "Eggersriet (SG),Grub (AR)" ...
 $ DateOfBirth          : chr [1:155] "15.06.1958" "15.06.1958" "24.02.1959" "24.02.1959" ...
 $ DateOfDeath          : logi [1:155] NA NA NA NA NA NA ...

2.3.2 Kantonsebene

2.3.2.1 Kantonale Parlamente (Legislative)

Code anzeigen
# TODO Die Kantonalen Abstimmungen finden nicht in allen kantonen gleichzeitg statt. Deshalb genauer die Räte/Konstellation zum Zeitpunkt der jeweiligen Abstimmung zu prüfen.


# Struktur der "schön formatierten" Exceldatei lässt keinen "simplen" Import zu.
# Header ist in Zeile 2 und nicht vollständig
# Daten (für Kantone) starten in Zeile 4 aber enden auf Zeile 29 bevor es mit Kommentaren und Fussnoten weitergeht 


# Header aus Zeile 2 für Spaltennamen lesen
tmp_header <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.05.01.03_KANTON_Kantonale_Parlamentswahlen.xlsx", 
                     sheet = 1, 
                     skip = 1,   # Header ist Zeile 2
                     n_max = 0) %>%
  names()

# Lücken im Header anpassen (i.e. erste beiden Spalten benennen)
tmp_header <- c("Kanton", "Leer", tmp_header)

tmp_name_map <- c("Kanton"        = "Kanton",
                  "Leer"          = "Leer",
                  "Wahljahr 5)"   = "Wahljahr",
                  "FDP 2)"        = "FDP",
                  "SP"            = "SP",
                  "SVP"           = "SVP",
                  "LPS 2)"        = "LPS",
                  "EVP"           = "EVP",
                  "CSP"           = "CSP",
                  "GLP"           = "GLP",
                  "Die Mitte 8)"  = "Mitte",
                  "CVP 3) 8)"     = "CVP",
                  "BDP 8)"        = "BDP",
                  "PdA"           = "PdA",
                  "PSA"           = "PSA",
                  "Grüne 9)"      = "Grüne",
                  "FGA"           = "FGA",
                  "Sol."          = "Sol.",
                  "EDU"           = "EDU",
                  "Lega"          = "Lega",
                  "MCG (MCR)"     = "MCR",
                  "Übrige 4)"     = "Übrige",
                  "Total"         = "Total")



# Automatisch ersetzen
tmp_header <- ifelse(tmp_header %in% names(tmp_name_map),
                     tmp_name_map[tmp_header],
                     tmp_header)

print(tmp_header)
 [1] "Kanton"   "Leer"     "Wahljahr" "FDP"      "SP"       "SVP"     
 [7] "LPS"      "EVP"      "CSP"      "GLP"      "Mitte"    "CVP"     
[13] "BDP"      "PdA"      "PSA"      "Grüne"    "FGA"      "Sol."    
[19] "EDU"      "Lega"     "MCR"      "Übrige"   "Total"   
Code anzeigen
# Daten ab Zeile 4 importieren
elec_kantonsparlament_raw <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.05.01.03_KANTON_Kantonale_Parlamentswahlen.xlsx", 
                 sheet = 1, 
                 skip = 3,         # überspringt die ersten 3 Zeilen
                 col_names = tmp_header)

# Schritt 4: Nur Zeilen behalten, in denen "Wahljahr" nicht NA ist
elec_kantonsparlament <- elec_kantonsparlament_raw  %>%
  filter(!is.na(Wahljahr)) %>% 
  select(-Leer)

# Ergebnis anzeigen
print(elec_kantonsparlament)
# A tibble: 26 × 22
   Kanton   Wahljahr FDP   SP    SVP   LPS   EVP   CSP   GLP   Mitte CVP   BDP  
   <chr>       <dbl> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
 1 Zürich       2023 29    36    46    *     7     *     24    11    *     *    
 2 Bern         2022 18    32    44    *     9     *     16    12    *     *    
 3 Luzern       2023 22    19    27    *     0     *     8     32    *     *    
 4 Uri 6)       2024 12    4     17    *     *     *     3     22    *     *    
 5 Schwyz       2024 19    14    38    *     0     *     5     23    *     *    
 6 Obwalden     2022 11    6     13    *     *     *     2     19    *     *    
 7 Nidwald…     2022 16    2     15    *     *     *     5     15    *     *    
 8 Glarus       2022 11    8     18    *     *     *     3     12    *     *    
 9 Zug          2022 18    8     18    *     *     *     6     19    *     *    
10 Freiburg     2021 23    21    18    *     0     4     3     26    *     *    
# ℹ 16 more rows
# ℹ 10 more variables: PdA <chr>, PSA <chr>, Grüne <chr>, FGA <chr>,
#   Sol. <chr>, EDU <chr>, Lega <chr>, MCR <chr>, Übrige <chr>, Total <dbl>

2.3.2.2 Kantonale Regierung (Exekutive)

Die Kantonalen Abstimmungen finden nicht in allen kantonen gleichzeitg statt. Deshalb genauer die Räte/Konstellation zum Zeitpunkt der jeweiligen Abstimmung zu prüfen.

Die Struktur der “schön formatierten” Exceldatei lässt keinen “simplen” Import zu.

  • Header ist in Zeile 2 und nicht vollständig

  • Daten (für Kantone) starten in Zeile 4 aber enden auf Zeile 29 bevor es mit Kommentaren und Fussnoten weitergeht.

2.3.2.2.1 Sheet 1: 2025
Code anzeigen
# Header aus Zeile 2 für Spaltennamen lesen
tmp_header <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.06.01_KANTON_Kantonale_Regierungswahlen.xlsx", 
                     sheet = 1, 
                     skip = 1,   # Header ist Zeile 2
                     n_max = 0) %>%
  names()

# Lücken im Header anpassen (i.e. erste Spalten benennen)
tmp_header <- c("Kanton", tmp_header)

tmp_name_map <- c("Kanton"        = "Kanton",
                  "Wahljahr 1"    = "Wahljahr",
                  "FDP 2"         = "FDP",
                  "SP"            = "SP",
                  "SVP"           = "SVP",
                  "LP 2"          = "LPS",
                  "EVP"           = "EVP",
                  "CSP"           = "CSP",
                  "GLP"           = "GLP",
                  "Die Mitte 3"   = "Mitte",
                  "CVP 3"         = "CVP",
                  "BDP 3"         = "BDP",
                  "PdA"           = "PdA",
                  "PSA"           = "PSA",
                  "Grüne 5"       = "Grüne",
                  "FGA"           = "FGA",
                  "Sol."          = "Sol.",
                  "Lega"          = "Lega",
                  "MCG (MCR)"     = "MCR",
                  "Übrige 4"      = "Übrige",
                  "Total"         = "Total")

# Automatisch ersetzen
tmp_header_neu <- ifelse(tmp_header %in% names(tmp_name_map),
                     tmp_name_map[tmp_header],
                     tmp_header)

print(tmp_header_neu)
 [1] "Kanton"   "Wahljahr" "FDP"      "SP"       "SVP"      "LPS"     
 [7] "EVP"      "CSP"      "GLP"      "Mitte"    "CVP"      "BDP"     
[13] "PdA"      "PSA"      "Grüne"    "FGA"      "Sol."     "Lega"    
[19] "MCR"      "Übrige"   "Total"   
Code anzeigen
# Daten ab Zeile 4 importieren
elec_kantonsregierung_raw <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.06.01_KANTON_Kantonale_Regierungswahlen.xlsx", 
                 sheet = 1, 
                 skip = 3,         # überspringt die ersten 3 Zeilen
                 col_names = tmp_header_neu)

# Schritt 4: Nur Zeilen behalten, in denen "Wahljahr" nicht NA ist
elec_kantonsregierung_2025 <- elec_kantonsregierung_raw  %>%
  filter(!is.na(Wahljahr)) %>%
  select(Kanton, where(~ {x <- suppressWarnings(as.numeric(.x))
    sum(x, na.rm = TRUE) > 0}))

# Ergebnis anzeigen
print(elec_kantonsregierung_2025)
# A tibble: 26 × 15
   Kanton   Wahljahr FDP   SP      SVP LPS   EVP   CSP   GLP   Mitte CVP   Grüne
   <chr>       <dbl> <chr> <chr> <dbl> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
 1 Zürich       2023 1     1         2 *     0     *     0     1     *     1    
 2 Bern         2022 1     2         2 *     0     *     *     1     *     1    
 3 Luzern       2023 1     1         1 *     *     *     0     2     *     0    
 4 Uri          2024 2     0         1 *     *     *     0     4     *     *    
 5 Schwyz       2024 2     0         3 *     *     *     *     2     *     *    
 6 Obwalden     2022 0     *         1 *     *     *     *     2     *     *    
 7 Nidwald…     2022 1     *         2 *     *     *     1     3     *     *    
 8 Glarus       2022 1     1         2 *     *     *     *     1     *     *    
 9 Zug          2022 2     *         2 *     *     *     0     3     *     0    
10 Freiburg     2021 2     1         1 *     *     0     0     2     *     1    
# ℹ 16 more rows
# ℹ 3 more variables: Lega <chr>, Übrige <chr>, Total <dbl>
2.3.2.2.2 Sheet 2: 2024
Code anzeigen
# Header aus Zeile 2 für Spaltennamen lesen
tmp_header <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.06.01_KANTON_Kantonale_Regierungswahlen.xlsx", 
                     sheet = 2, 
                     skip = 1,   # Header ist Zeile 2
                     n_max = 0) %>%
  names()

# Lücken im Header anpassen (i.e. erste Spalten benennen)
tmp_header <- c("Kanton", tmp_header)
cat(paste0('"', tmp_header, '"', ","), sep = "\n")
"Kanton",
"Wahljahr 1",
"FDP 2",
"SP",
"SVP",
"LP 2",
"EVP",
"CSP",
"GLP",
"Die Mitte 3",
"CVP 3",
"BDP 3",
"PdA",
"PSA",
"Grüne 5",
"FGA",
"Sol.",
"Lega",
"MCG (MCR)",
"Übrige 4",
"Total",
Code anzeigen
tmp_name_map <- c("Kanton"        = "Kanton",
                  "Wahljahr 1"    = "Wahljahr",
                  "FDP 2"         = "FDP",
                  "SP"            = "SP",
                  "SVP"           = "SVP",
                  "LP 2"          = "LPS",
                  "EVP"           = "EVP",
                  "CSP"           = "CSP",
                  "GLP"           = "GLP",
                  "Die Mitte 3"   = "Mitte",
                  "CVP 3"         = "CVP",
                  "BDP 3"         = "BDP",
                  "PdA"           = "PdA",
                  "PSA"           = "PSA",
                  "Grüne 5"       = "Grüne",
                  "FGA"           = "FGA",
                  "Sol."          = "Sol.",
                  "Lega"          = "Lega",
                  "MCG (MCR)"     = "MCR",
                  "Übrige 4"      = "Übrige",
                  "Total"         = "Total")

# Automatisch ersetzen
tmp_header_neu <- ifelse(tmp_header %in% names(tmp_name_map),
                     tmp_name_map[tmp_header],
                     tmp_header)

print(tmp_header_neu)
 [1] "Kanton"   "Wahljahr" "FDP"      "SP"       "SVP"      "LPS"     
 [7] "EVP"      "CSP"      "GLP"      "Mitte"    "CVP"      "BDP"     
[13] "PdA"      "PSA"      "Grüne"    "FGA"      "Sol."     "Lega"    
[19] "MCR"      "Übrige"   "Total"   
Code anzeigen
# Daten ab Zeile 4 importieren
elec_kantonsregierung_raw <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.06.01_KANTON_Kantonale_Regierungswahlen.xlsx", 
                 sheet = 2, 
                 skip = 3,         # überspringt die ersten 3 Zeilen
                 col_names = tmp_header_neu)

# Schritt 4: Nur Zeilen behalten, in denen "Wahljahr" nicht NA ist
elec_kantonsregierung_2024 <- elec_kantonsregierung_raw  %>%
  filter(!is.na(Wahljahr)) %>%
  select(Kanton, where(~ {x <- suppressWarnings(as.numeric(.x))
    sum(x, na.rm = TRUE) > 0}))

# Ergebnis anzeigen
print(elec_kantonsregierung_2024)
# A tibble: 26 × 15
   Kanton   Wahljahr FDP   SP      SVP LPS   EVP   CSP   GLP   Mitte CVP   Grüne
   <chr>       <dbl> <chr> <chr> <dbl> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
 1 Zürich       2023 1     1         2 *     0     *     0     1     *     1    
 2 Bern         2022 1     2         2 *     0     *     *     1     *     1    
 3 Luzern       2023 1     1         1 *     *     *     0     2     *     0    
 4 Uri          2024 2     0         1 *     *     *     0     4     *     *    
 5 Schwyz       2024 2     0         3 *     *     *     *     2     *     *    
 6 Obwalden     2022 0     *         1 *     *     *     *     2     *     *    
 7 Nidwald…     2022 1     *         2 *     *     *     1     3     *     *    
 8 Glarus       2022 1     1         2 *     *     *     *     1     *     *    
 9 Zug          2022 2     *         2 *     *     *     0     3     *     0    
10 Freiburg     2021 2     1         1 *     *     0     0     2     *     1    
# ℹ 16 more rows
# ℹ 3 more variables: Lega <chr>, Übrige <chr>, Total <dbl>
2.3.2.2.3 Sheet 3: 2023
Code anzeigen
# Header aus Zeile 2 für Spaltennamen lesen
tmp_header <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.06.01_KANTON_Kantonale_Regierungswahlen.xlsx", 
                     sheet = 3, 
                     skip = 1,   # Header ist Zeile 2
                     n_max = 0) %>%
  names()

# Lücken im Header anpassen (i.e. erste Spalten benennen)
tmp_header <- c("Kanton", tmp_header)
cat(paste0('"', tmp_header, '"', ","), sep = "\n")
"Kanton",
"Wahljahr 1)",
"FDP 2)",
"SP",
"SVP",
"LP 2)",
"EVP",
"CSP",
"GLP",
"Die Mitte 3)",
"CVP 3)",
"BDP 5)",
"PdA",
"PSA",
"Grüne 6)",
"FGA",
"Sol.",
"Lega",
"MCR",
"Übrige 4)",
"Total",
Code anzeigen
tmp_name_map <- c("Kanton"        = "Kanton",
                  "Wahljahr 1)"    = "Wahljahr",
                  "FDP 2)"         = "FDP",
                  "SP"            = "SP",
                  "SVP"           = "SVP",
                  "LP 2)"          = "LPS",
                  "EVP"           = "EVP",
                  "CSP"           = "CSP",
                  "GLP"           = "GLP",
                  "Die Mitte 3)"   = "Mitte",
                  "CVP 3)"         = "CVP",
                  "BDP 3)"         = "BDP",
                  "PdA"           = "PdA",
                  "PSA"           = "PSA",
                  "Grüne 5)"       = "Grüne",
                  "FGA"           = "FGA",
                  "Sol."          = "Sol.",
                  "Lega"          = "Lega",
                  "MCG (MCR)"     = "MCR",
                  "Übrige 4)"      = "Übrige",
                  "Total"         = "Total")

# Automatisch ersetzen
tmp_header_neu <- ifelse(tmp_header %in% names(tmp_name_map),
                     tmp_name_map[tmp_header],
                     tmp_header)

print(tmp_header_neu)
 [1] "Kanton"   "Wahljahr" "FDP"      "SP"       "SVP"      "LPS"     
 [7] "EVP"      "CSP"      "GLP"      "Mitte"    "CVP"      "BDP 5)"  
[13] "PdA"      "PSA"      "Grüne 6)" "FGA"      "Sol."     "Lega"    
[19] "MCR"      "Übrige"   "Total"   
Code anzeigen
# Daten ab Zeile 4 importieren
elec_kantonsregierung_raw <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.06.01_KANTON_Kantonale_Regierungswahlen.xlsx", 
                 sheet = 3, 
                 skip = 3,         # überspringt die ersten 3 Zeilen
                 col_names = tmp_header_neu)

# Schritt 4: Nur Zeilen behalten, in denen "Wahljahr" nicht NA ist
elec_kantonsregierung_2023 <- elec_kantonsregierung_raw  %>%
  filter(!is.na(Wahljahr)) %>%
  select(Kanton, where(~ {x <- suppressWarnings(as.numeric(.x))
    sum(x, na.rm = TRUE) > 0}))

# Ergebnis anzeigen
print(elec_kantonsregierung_2023)
# A tibble: 26 × 15
   Kanton    Wahljahr FDP   SP      SVP LPS   EVP   CSP   GLP   Mitte CVP  
   <chr>        <dbl> <chr> <chr> <dbl> <chr> <chr> <chr> <chr> <chr> <chr>
 1 Zürich        2023 1     1         2 *     0     *     0     1     *    
 2 Bern          2022 1     2         2 *     0     *     *     1     *    
 3 Luzern        2023 1     1         1 *     *     *     0     2     *    
 4 Uri           2020 2     1         1 *     *     *     *     *     3    
 5 Schwyz        2020 2     0         3 *     *     *     0     2     *    
 6 Obwalden      2022 0     *         1 *     *     *     *     2     *    
 7 Nidwalden     2022 1     *         2 *     *     *     1     3     *    
 8 Glarus        2022 2     1         1 *     *     *     *     1     *    
 9 Zug           2022 2     *         2 *     *     *     0     3     *    
10 Freiburg      2021 2     1         1 *     *     0     0     2     *    
# ℹ 16 more rows
# ℹ 4 more variables: `Grüne 6)` <chr>, Lega <chr>, Übrige <chr>, Total <dbl>
2.3.2.2.4 Sheet 4: 2022
Code anzeigen
# Header aus Zeile 2 für Spaltennamen lesen
tmp_header <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.06.01_KANTON_Kantonale_Regierungswahlen.xlsx", 
                     sheet = 4, 
                     skip = 1,   # Header ist Zeile 2
                     n_max = 0) %>%
  names()

# Lücken im Header anpassen (i.e. erste Spalten benennen)
tmp_header <- c("Kanton", tmp_header)
cat(paste0('"', tmp_header, '"', ","), sep = "\n")
"Kanton",
"Wahljahr 1)",
"FDP 2)",
"SP",
"SVP",
"LP 2)",
"EVP",
"CSP",
"GLP",
"Die Mitte 3)",
"CVP 3)",
"BDP 5)",
"PdA",
"PSA",
"Grüne 6)",
"FGA",
"Sol.",
"Lega",
"MCR",
"Übrige 4)",
"Total",
Code anzeigen
tmp_name_map <- c("Kanton"        = "Kanton",
                  "Wahljahr 1)"    = "Wahljahr",
                  "FDP 2)"         = "FDP",
                  "SP"            = "SP",
                  "SVP"           = "SVP",
                  "LP 2)"          = "LPS",
                  "EVP"           = "EVP",
                  "CSP"           = "CSP",
                  "GLP"           = "GLP",
                  "Die Mitte 3)"   = "Mitte",
                  "CVP 3)"         = "CVP",
                  "BDP 3)"         = "BDP",
                  "PdA"           = "PdA",
                  "PSA"           = "PSA",
                  "Grüne 6)"       = "Grüne",
                  "FGA"           = "FGA",
                  "Sol."          = "Sol.",
                  "Lega"          = "Lega",
                  "MCR"     = "MCR",
                  "Übrige 4)"      = "Übrige",
                  "Total"         = "Total")

# Automatisch ersetzen
tmp_header_neu <- ifelse(tmp_header %in% names(tmp_name_map),
                     tmp_name_map[tmp_header],
                     tmp_header)

print(tmp_header_neu)
 [1] "Kanton"   "Wahljahr" "FDP"      "SP"       "SVP"      "LPS"     
 [7] "EVP"      "CSP"      "GLP"      "Mitte"    "CVP"      "BDP 5)"  
[13] "PdA"      "PSA"      "Grüne"    "FGA"      "Sol."     "Lega"    
[19] "MCR"      "Übrige"   "Total"   
Code anzeigen
# Daten ab Zeile 4 importieren
elec_kantonsregierung_raw <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.06.01_KANTON_Kantonale_Regierungswahlen.xlsx", 
                 sheet = 4, 
                 skip = 3,         # überspringt die ersten 3 Zeilen
                 col_names = tmp_header_neu)

# Schritt 4: Nur Zeilen behalten, in denen "Wahljahr" nicht NA ist
elec_kantonsregierung_2022 <- elec_kantonsregierung_raw  %>%
  filter(!is.na(Wahljahr)) %>%
  select(Kanton, where(~ {x <- suppressWarnings(as.numeric(.x))
    sum(x, na.rm = TRUE) > 0}))

# Ergebnis anzeigen
print(elec_kantonsregierung_2022)
# A tibble: 26 × 15
   Kanton   Wahljahr FDP   SP      SVP LPS   CSP   GLP   Mitte CVP   Grüne Lega 
   <chr>       <dbl> <chr> <chr> <dbl> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
 1 Zürich       2019 1     1         2 *     *     *     *     1     1     *    
 2 Bern         2022 1     2         2 *     *     *     1     *     1     *    
 3 Luzern       2019 1     0         1 *     *     0     *     2     0     *    
 4 Uri          2020 2     1         1 *     *     *     *     3     *     *    
 5 Schwyz       2020 2     0         3 *     *     0     2     *     0     *    
 6 Obwalden     2022 0     *         1 *     *     *     2     *     *     *    
 7 Nidwald…     2022 1     *         2 *     *     1     3     *     *     *    
 8 Glarus       2022 2     1         1 *     *     *     1     *     *     *    
 9 Zug          2022 2     *         2 *     *     0     3     *     0     *    
10 Freiburg     2021 2     1         1 *     0     0     2     *     1     *    
# ℹ 16 more rows
# ℹ 3 more variables: MCR <chr>, Übrige <chr>, Total <dbl>
2.3.2.2.5 Sheet 5: 2021
Code anzeigen
# Header aus Zeile 2 für Spaltennamen lesen
tmp_header <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.06.01_KANTON_Kantonale_Regierungswahlen.xlsx", 
                     sheet = 5, 
                     skip = 1,   # Header ist Zeile 2
                     n_max = 0) %>%
  names()

# Lücken im Header anpassen (i.e. erste Spalten benennen)
tmp_header <- c("Kanton", tmp_header)
cat(paste0('"', tmp_header, '"', ","), sep = "\n")
"Kanton",
"Wahljahr 1)",
"FDP 2)",
"SP",
"SVP",
"LP 2)",
"EVP",
"CSP",
"GLP",
"Die Mitte 3)",
"CVP 3)",
"BDP 5)",
"PdA",
"PSA",
"GPS",
"FGA",
"Sol.",
"Lega",
"MCR",
"Übrige 4)",
"Total",
Code anzeigen
tmp_name_map <- c("Kanton"        = "Kanton",
                  "Wahljahr 1)"    = "Wahljahr",
                  "FDP 2)"         = "FDP",
                  "SP"            = "SP",
                  "SVP"           = "SVP",
                  "LP 2)"          = "LPS",
                  "EVP"           = "EVP",
                  "CSP"           = "CSP",
                  "GLP"           = "GLP",
                  "Die Mitte 3)"   = "Mitte",
                  "CVP 3)"         = "CVP",
                  "BDP 5)"         = "BDP",
                  "PdA"           = "PdA",
                  "PSA"           = "PSA",
                  "GPS"       = "Grüne",
                  "FGA"           = "FGA",
                  "Sol."          = "Sol.",
                  "Lega"          = "Lega",
                  "MCR"     = "MCR",
                  "Übrige 4)"      = "Übrige",
                  "Total"         = "Total")

# Automatisch ersetzen
tmp_header_neu <- ifelse(tmp_header %in% names(tmp_name_map),
                     tmp_name_map[tmp_header],
                     tmp_header)

print(tmp_header_neu)
 [1] "Kanton"   "Wahljahr" "FDP"      "SP"       "SVP"      "LPS"     
 [7] "EVP"      "CSP"      "GLP"      "Mitte"    "CVP"      "BDP"     
[13] "PdA"      "PSA"      "Grüne"    "FGA"      "Sol."     "Lega"    
[19] "MCR"      "Übrige"   "Total"   
Code anzeigen
# Daten ab Zeile 4 importieren
elec_kantonsregierung_raw <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.06.01_KANTON_Kantonale_Regierungswahlen.xlsx", 
                 sheet = 5, 
                 skip = 3,         # überspringt die ersten 3 Zeilen
                 col_names = tmp_header_neu)

# Schritt 4: Nur Zeilen behalten, in denen "Wahljahr" nicht NA ist
elec_kantonsregierung_2021 <- elec_kantonsregierung_raw  %>%
  filter(!is.na(Wahljahr)) %>%
  select(Kanton, where(~ {x <- suppressWarnings(as.numeric(.x))
    sum(x, na.rm = TRUE) > 0}))

# Ergebnis anzeigen
print(elec_kantonsregierung_2021)
# A tibble: 26 × 16
   Kanton   Wahljahr FDP   SP      SVP LPS   CSP   GLP   Mitte CVP   BDP   Grüne
   <chr>       <dbl> <chr> <chr> <dbl> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
 1 Zürich       2019 1     1         2 *     *     *     *     1     0     1    
 2 Bern         2018 1     2         2 *     *     *     *     *     1     1    
 3 Luzern       2019 1     0         1 *     *     0     *     2     *     0    
 4 Uri          2020 2     1         1 *     *     *     *     3     *     *    
 5 Schwyz       2020 2     0         3 *     *     *     *     2     *     0    
 6 Obwalden     2018 1     *         1 *     *     *     *     1     *     *    
 7 Nidwald…     2018 2     *         2 *     *     *     *     3     *     *    
 8 Glarus       2018 2     1         1 *     *     *     *     0     1     *    
 9 Zug          2018 2     0         2 *     *     0     *     3     *     0    
10 Freiburg     2021 2     1         1 *     0     0     2     *     *     1    
# ℹ 16 more rows
# ℹ 4 more variables: Lega <chr>, MCR <chr>, Übrige <chr>, Total <dbl>
2.3.2.2.6 Sheet 6: 2020
Code anzeigen
# Header aus Zeile 2 für Spaltennamen lesen
tmp_header <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.06.01_KANTON_Kantonale_Regierungswahlen.xlsx", 
                     sheet = 6, 
                     skip = 1,   # Header ist Zeile 2
                     n_max = 0) %>%
  names()

# Lücken im Header anpassen (i.e. erste Spalten benennen)
tmp_header <- c("Kanton", tmp_header)
cat(paste0('"', tmp_header, '"', ","), sep = "\n")
"Kanton",
"Wahljahr 1)",
"FDP 2)",
"CVP 3)",
"SP",
"SVP",
"Dem.",
"LP 2)",
"LdU",
"EVP",
"CSP",
"DSP",
"GLP",
"BDP",
"PdA",
"PSA",
"POCH",
"GPS",
"FGA",
"Sol.",
"SD",
"Rep.",
"EDU",
"FPS",
"Lega",
"MCR",
"LS",
"JB",
"Front",
"Grüt",
"Übrige 4)",
"Total",
Code anzeigen
tmp_name_map <- c("Kanton"         = "Kanton",
                  "Wahljahr 1)"    = "Wahljahr",
                  "FDP 2)"         = "FDP",
                  "CVP 3)"         = "CVP",
                  "SP"             = "SP",
                  "SVP"            = "SVP",
                  "LP 2)"          = "LPS",
                  "CSP"            = "CSP",
                  "GLP"            = "GLP",
                  "BDP"            = "BDP",
                  "GPS"            = "Grüne",
                  "Lega"           = "Lega",
                  "MCR"            = "MCR",
                  "Übrige 4)"      = "Übrige",
                  "Total"          = "Total")

# Automatisch ersetzen
tmp_header_neu <- ifelse(tmp_header %in% names(tmp_name_map),
                     tmp_name_map[tmp_header],
                     tmp_header)

print(tmp_header_neu)
 [1] "Kanton"   "Wahljahr" "FDP"      "CVP"      "SP"       "SVP"     
 [7] "Dem."     "LPS"      "LdU"      "EVP"      "CSP"      "DSP"     
[13] "GLP"      "BDP"      "PdA"      "PSA"      "POCH"     "Grüne"   
[19] "FGA"      "Sol."     "SD"       "Rep."     "EDU"      "FPS"     
[25] "Lega"     "MCR"      "LS"       "JB"       "Front"    "Grüt"    
[31] "Übrige"   "Total"   
Code anzeigen
# Daten ab Zeile 4 importieren
elec_kantonsregierung_raw <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.06.01_KANTON_Kantonale_Regierungswahlen.xlsx", 
                 sheet = 6, 
                 skip = 3,         # überspringt die ersten 3 Zeilen
                 col_names = tmp_header_neu)

# Schritt 4: Nur Zeilen behalten, in denen "Wahljahr" nicht NA ist
elec_kantonsregierung_2020 <- elec_kantonsregierung_raw  %>%
  filter(!is.na(Wahljahr)) %>%
  select(Kanton, where(~ {x <- suppressWarnings(as.numeric(.x))
    sum(x, na.rm = TRUE) > 0}))


# Ergebnis anzeigen
print(elec_kantonsregierung_2020)
# A tibble: 26 × 15
   Kanton   Wahljahr FDP   CVP   SP      SVP LPS   CSP   GLP   BDP   Grüne Lega 
   <chr>       <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr> <chr> <chr> <chr>
 1 Zürich       2019 1     1     1         2 *     *     *     0     1     *    
 2 Bern         2018 1     *     2         2 *     *     *     1     1     *    
 3 Luzern       2019 1     2     0         1 *     *     0     *     0     *    
 4 Uri          2020 2     3     1         1 *     *     *     *     *     *    
 5 Schwyz       2020 2     2     0         3 *     *     *     *     0     *    
 6 Obwalden     2018 1     1     *         1 *     *     *     *     *     *    
 7 Nidwald…     2018 2     3     *         2 *     *     *     *     *     *    
 8 Glarus       2018 2     1     0         1 *     *     *     1     *     *    
 9 Zug          2018 2     3     0         2 *     *     0     *     0     *    
10 Freiburg     2016 2     3     2         0 *     0     0     *     0     *    
# ℹ 16 more rows
# ℹ 3 more variables: MCR <chr>, Übrige <chr>, Total <dbl>
2.3.2.2.7 Sheet 7: 2019
Code anzeigen
# Header aus Zeile 2 für Spaltennamen lesen
tmp_header <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.06.01_KANTON_Kantonale_Regierungswahlen.xlsx", 
                     sheet = 7, 
                     skip = 1,   # Header ist Zeile 2
                     n_max = 0) %>%
  names()

# Lücken im Header anpassen (i.e. erste Spalten benennen)
tmp_header <- c("Kanton", tmp_header)
cat(paste0('"', tmp_header, '"', ","), sep = "\n")
"Kanton",
"Wahljahr 1)",
"FDP 2)",
"CVP 3)",
"SP",
"SVP",
"Dem.",
"LP 2)",
"LdU",
"EVP",
"CSP",
"DSP",
"GLP",
"BDP",
"PdA",
"PSA",
"POCH",
"GP",
"FGA",
"Sol.",
"SD",
"Rep.",
"EDU",
"FPS",
"Lega",
"MCR",
"LS",
"JB",
"Front",
"Grüt",
"Übrige 4)",
"Total",
Code anzeigen
tmp_name_map <- c("Kanton"        = "Kanton",
                  "Wahljahr 1)"    = "Wahljahr",
                  "FDP 2)"         = "FDP",
                  "SP"            = "SP",
                  "SVP"           = "SVP",
                  "LP 2)"          = "LPS",
                  "EVP"           = "EVP",
                  "CSP"           = "CSP",
                  "GLP"           = "GLP",
                  "Die Mitte 3)"   = "Mitte",
                  "CVP 3)"         = "CVP",
                  "BDP 3)"         = "BDP",
                  "PdA"           = "PdA",
                  "PSA"           = "PSA",
                  "Grüne 5)"       = "Grüne",
                  "FGA"           = "FGA",
                  "Sol."          = "Sol.",
                  "Lega"          = "Lega",
                  "MCG (MCR)"     = "MCR",
                  "Übrige 4)"      = "Übrige",
                  "Total"         = "Total")

# Automatisch ersetzen
tmp_header_neu <- ifelse(tmp_header %in% names(tmp_name_map),
                     tmp_name_map[tmp_header],
                     tmp_header)

print(tmp_header_neu)
 [1] "Kanton"   "Wahljahr" "FDP"      "CVP"      "SP"       "SVP"     
 [7] "Dem."     "LPS"      "LdU"      "EVP"      "CSP"      "DSP"     
[13] "GLP"      "BDP"      "PdA"      "PSA"      "POCH"     "GP"      
[19] "FGA"      "Sol."     "SD"       "Rep."     "EDU"      "FPS"     
[25] "Lega"     "MCR"      "LS"       "JB"       "Front"    "Grüt"    
[31] "Übrige"   "Total"   
Code anzeigen
# Daten ab Zeile 4 importieren
elec_kantonsregierung_raw <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.06.01_KANTON_Kantonale_Regierungswahlen.xlsx", 
                 sheet = 7, 
                 skip = 3,         # überspringt die ersten 3 Zeilen
                 col_names = tmp_header_neu)

# Schritt 4: Nur Zeilen behalten, in denen "Wahljahr" nicht NA ist
elec_kantonsregierung_2019 <- elec_kantonsregierung_raw  %>%
  filter(!is.na(Wahljahr)) %>%
  select(Kanton, where(~ {x <- suppressWarnings(as.numeric(.x))
    sum(x, na.rm = TRUE) > 0}))

# Ergebnis anzeigen
view(elec_kantonsregierung_2019)
2.3.2.2.8 Sheets konsolidieren & Change log erstellen
Code anzeigen
# Alle Dataframes in Liste packen
df_list <- list(elec_kantonsregierung_2019,
                elec_kantonsregierung_2020,
                elec_kantonsregierung_2021,
                elec_kantonsregierung_2022,
                elec_kantonsregierung_2023,
                elec_kantonsregierung_2024,
                elec_kantonsregierung_2025)

# Alle Dataframes zusammenführen (Vereinigung aller Spalten)
df_all <- bind_rows(df_list)

# Filter auf relevante Wahljahre setzen (2015, da Legislatur 4-5 Jahre dauert und später der Zeitraum ab 2019 geprüft wird)
df_all <- df_all %>%
  filter(Wahljahr >= 2015, Wahljahr <= 2025)

# Hilfsfuntion um alle Einträge in numerische umzuwandeln
make_numeric <- function(x) suppressWarnings(as.numeric(x))

# Funktion auf alle Spalten ausser Kanton & Wahljahr anwenden.
df_all <- df_all %>%
  mutate(across(-c(Kanton, Wahljahr), make_numeric))

# Pro Kanton+Wahljahr auf eine Zeile reduzieren
df_all <- df_all %>%
  group_by(Kanton, Wahljahr) %>%
  summarise(across(everything(), ~ coalesce(last(na.omit(.)), NA_real_)), .groups = "drop")

# Alle Kantone und Wahljahre eruieren/bestimmen, die in den Daten vorkommen
vektor_kanton <- unique(df_all$Kanton)
vektor_wahljahr <- 2015:2025

# Vollständigen Grid erzeugen
df_grid <- expand_grid(Kanton = vektor_kanton,
                       Wahljahr = vektor_wahljahr)

# Mit den Daten verknüpfen
df_all <- left_join(df_grid,
                    df_all,
                    by = c("Kanton", "Wahljahr"))


elec_kantonsregierung_final <- df_all %>%
  arrange(Kanton, Wahljahr) %>%
  group_by(Kanton) %>%
  fill(everything(), .direction = "down") %>%
  ungroup()

2.3.3 Gemeindeebene

2.3.3.1 Exekutiven der statistischen Städte

  • 1 Excelsheet / Jahr

  • Header zweizeilig mit Start in Zeile –> 3 skip = 2, n_max = 2,

  • Teils verbundene Header-Zeilen (vertikal/horizontal)

  • Cluster Einwohnerzahlen unterteilt Daten

  • Parteinamen uneinheitlich aufgrund von Fussnoten

–> Datenformat “wide” zuerst bereinigen und ins “long” Format bringen

Initialer Code für Einzelimport (ohne Function)

Code anzeigen
#| eval: false
#| echo: true


###################################################################
########## DATEN IMPORTIEREN ######################################
########## Header definieren ######################################
###################################################################


# Headerzeilen einlesen
header <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.07.01_GEMEINDE_Die Exekutiven der statistischen Städte.xlsx",
                     sheet = 2,
                     skip = 2,
                     n_max = 2,
                     col_names = FALSE)

# Daten  einlesen
daten_raw <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.07.01_GEMEINDE_Die Exekutiven der statistischen Städte.xlsx",
                    sheet= 2,
                    skip = 5,
                    col_names = FALSE)


# Die Spaltennamen zusammensetzen
# Leere Zellen in der ersten Headerzeile mit Werten von rechts auffüllen (falls nötig)
header_filled <- as.data.frame(t(header))   # t() "matrix transpose"
header_filled <- fill(header_filled,        
                      V1,                   # Header Spalte 1 (V1) auffüllen
                      .direction = "down")  # Werte nach unten übernehmen
header_filled <- t(header_filled)           # t() nochmals (zurück)

# Spaltennamen kombinieren
spaltennamen <- paste(header_filled[1, ],
                      header_filled[2, ],
                      sep = "_")
spaltennamen <- gsub("_NA|NA_","", spaltennamen) # Entfernt überflüssige NAs

# Spaltennamen zuweisen
colnames(daten_raw) <- spaltennamen


# Ergebnis: Dataframe mit korrekten Spaltennamen
head(daten_raw)
# A tibble: 6 × 54
  `Kantons-Nr.` Kanton `Gemeinde-Nr.` Gemeinde Wahljahr Einwohner `FDP 1_Frauen`
  <chr>         <chr>           <dbl> <chr>       <dbl>     <dbl> <chr>         
1 ≥ 100'000 Ei… <NA>               NA <NA>           NA        NA <NA>          
2 1             ZH                230 Wintert…     2022    115129 0             
3 1             ZH                261 Zürich       2022    423193 0             
4 2             BE                351 Bern         2020    134591 -             
5 12            BS               2701 Basel        2020    173232 -             
6 22            VD               5586 Lausanne     2021    140202 0             
# ℹ 47 more variables: `FDP 1_Männer` <chr>, `FDP 1_Total` <chr>,
#   `Die Mitte 2_Frauen` <chr>, `Die Mitte 2_Männer` <chr>,
#   `Die Mitte 2_Total` <chr>, `CVP 2_Frauen` <chr>, `CVP 2_Männer` <chr>,
#   `CVP 2_Total` <chr>, SP_Frauen <chr>, SP_Männer <chr>, SP_Total <chr>,
#   SVP_Frauen <chr>, SVP_Männer <chr>, SVP_Total <chr>, `LP 1_Frauen` <chr>,
#   `LP 1_Männer` <chr>, `LP 1_Total` <chr>, EVP_Frauen <chr>,
#   EVP_Männer <chr>, EVP_Total <chr>, CSP_Frauen <chr>, CSP_Männer <chr>, …
Code anzeigen
###################################################################
########## TRANSPONIEREN ##########################################
########## Gemeindegrössen Cluster ################################
###################################################################


# Cluster Einwohnerzahl als Spalte verwenden
daten_wide <- daten_raw %>%
  mutate(Gemeindegrösse_Cluster =            # Name der neuen Spalte
           ifelse(
             is.na(                          # 4) auf NA prüfen
               suppressWarnings(             # 3) Warnung aus (z.B. "≥ ..Einw..")
               as.numeric(                   # 2) Wert als Zahl 
               gsub("'", "", `Kantons-Nr.`)  # 1) Tausender Zeichen (') entfernen
             ))),
             as.character(`Kantons-Nr.`),    # 5) Wert von "Kantons-Nr" oder 
             NA_character_
           ))

# Ansicht (Snapshot) nach transponieren (vor auffülen)
daten_wide[1:10,                    # 10 Zeilen
           c(1:10,                  # 10 Spalten +
             ncol(daten_wide))]     # Letzte Spalte
# A tibble: 10 × 11
   `Kantons-Nr.`               Kanton `Gemeinde-Nr.` Gemeinde Wahljahr Einwohner
   <chr>                       <chr>           <dbl> <chr>       <dbl>     <dbl>
 1 ≥ 100'000 Einwohnerinnen u… <NA>               NA <NA>           NA        NA
 2 1                           ZH                230 Wintert…     2022    115129
 3 1                           ZH                261 Zürich       2022    423193
 4 2                           BE                351 Bern         2020    134591
 5 12                          BS               2701 Basel        2020    173232
 6 22                          VD               5586 Lausanne     2021    140202
 7 25                          GE               6621 Genève       2020    203951
 8 50'000-99'999 Einwohnerinn… <NA>               NA <NA>           NA        NA
 9 2                           BE                371 Biel/Bi…     2020     55602
10 3                           LU               1061 Luzern       2020     82257
# ℹ 5 more variables: `FDP 1_Frauen` <chr>, `FDP 1_Männer` <chr>,
#   `FDP 1_Total` <chr>, `Die Mitte 2_Frauen` <chr>,
#   Gemeindegrösse_Cluster <chr>
Code anzeigen
                                    # ncol() von df daten_wide --> Anz. Spalten
                                    # Anzahl Spalten = Position der letzten Spalte


# 2. Cluster nach unten auffüllen
daten_wide <- daten_wide %>%
  fill(Gemeindegrösse_Cluster, .direction = "down")


# Ansicht (Snapshot) nach transponieren & auffülen
daten_wide[1:10,                    # 10 Zeilen
           c(1:10,                  # 10 Spalten +
             ncol(daten_wide))]     # Letzte Spalte
# A tibble: 10 × 11
   `Kantons-Nr.`               Kanton `Gemeinde-Nr.` Gemeinde Wahljahr Einwohner
   <chr>                       <chr>           <dbl> <chr>       <dbl>     <dbl>
 1 ≥ 100'000 Einwohnerinnen u… <NA>               NA <NA>           NA        NA
 2 1                           ZH                230 Wintert…     2022    115129
 3 1                           ZH                261 Zürich       2022    423193
 4 2                           BE                351 Bern         2020    134591
 5 12                          BS               2701 Basel        2020    173232
 6 22                          VD               5586 Lausanne     2021    140202
 7 25                          GE               6621 Genève       2020    203951
 8 50'000-99'999 Einwohnerinn… <NA>               NA <NA>           NA        NA
 9 2                           BE                371 Biel/Bi…     2020     55602
10 3                           LU               1061 Luzern       2020     82257
# ℹ 5 more variables: `FDP 1_Frauen` <chr>, `FDP 1_Männer` <chr>,
#   `FDP 1_Total` <chr>, `Die Mitte 2_Frauen` <chr>,
#   Gemeindegrösse_Cluster <chr>
Code anzeigen
                                    # ncol() von df daten_wide --> Anz. Spalten
                                    # Anzahl Spalten = Position der letzten Spalte


# Herausfiltern von Zeilen mit NICHT-numerischem Wert in "Kantons-Nr."
daten_wide <- daten_wide %>%
    filter(!is.na(suppressWarnings(as.numeric(gsub("'", "", `Kantons-Nr.`)))))


# Ansicht (Snapshot) nach transponieren & auffüllen
daten_wide[1:10,                    # 10 Zeilen
           c(1:10,                  # 10 Spalten +
             ncol(daten_wide))]     # Letzte Spalte
# A tibble: 10 × 11
   `Kantons-Nr.` Kanton `Gemeinde-Nr.` Gemeinde    Wahljahr Einwohner
   <chr>         <chr>           <dbl> <chr>          <dbl>     <dbl>
 1 1             ZH                230 Winterthur      2022    115129
 2 1             ZH                261 Zürich          2022    423193
 3 2             BE                351 Bern            2020    134591
 4 12            BS               2701 Basel           2020    173232
 5 22            VD               5586 Lausanne        2021    140202
 6 25            GE               6621 Genève          2020    203951
 7 2             BE                371 Biel/Bienne     2020     55602
 8 3             LU               1061 Luzern          2020     82257
 9 17            SG               3203 St. Gallen      2020     76090
10 21            TI               5192 Lugano          2021     62315
# ℹ 5 more variables: `FDP 1_Frauen` <chr>, `FDP 1_Männer` <chr>,
#   `FDP 1_Total` <chr>, `Die Mitte 2_Frauen` <chr>,
#   Gemeindegrösse_Cluster <chr>
Code anzeigen
                                    # ncol() von df daten_wide --> Anz. Spalten
                                    # Anzahl Spalten = Position der letzten Spalte

# Ergebnis prüfen
head(daten_wide)
# A tibble: 6 × 55
  `Kantons-Nr.` Kanton `Gemeinde-Nr.` Gemeinde Wahljahr Einwohner `FDP 1_Frauen`
  <chr>         <chr>           <dbl> <chr>       <dbl>     <dbl> <chr>         
1 1             ZH                230 Wintert…     2022    115129 0             
2 1             ZH                261 Zürich       2022    423193 0             
3 2             BE                351 Bern         2020    134591 -             
4 12            BS               2701 Basel        2020    173232 -             
5 22            VD               5586 Lausanne     2021    140202 0             
6 25            GE               6621 Genève       2020    203951 -             
# ℹ 48 more variables: `FDP 1_Männer` <chr>, `FDP 1_Total` <chr>,
#   `Die Mitte 2_Frauen` <chr>, `Die Mitte 2_Männer` <chr>,
#   `Die Mitte 2_Total` <chr>, `CVP 2_Frauen` <chr>, `CVP 2_Männer` <chr>,
#   `CVP 2_Total` <chr>, SP_Frauen <chr>, SP_Männer <chr>, SP_Total <chr>,
#   SVP_Frauen <chr>, SVP_Männer <chr>, SVP_Total <chr>, `LP 1_Frauen` <chr>,
#   `LP 1_Männer` <chr>, `LP 1_Total` <chr>, EVP_Frauen <chr>,
#   EVP_Männer <chr>, EVP_Total <chr>, CSP_Frauen <chr>, CSP_Männer <chr>, …
Code anzeigen
###################################################################
########## TRANSPONIEREN ##########################################
########## Frauen, Männer, Total & Parteien ## ####################
###################################################################


daten_wide[1:18,                    # 10 Zeilen
           c(1:18,                  # 10 Spalten +
             ncol(daten_wide))]     # Letzte Spalte
# A tibble: 18 × 19
   `Kantons-Nr.` Kanton `Gemeinde-Nr.` Gemeinde      Wahljahr Einwohner
   <chr>         <chr>           <dbl> <chr>            <dbl>     <dbl>
 1 1             ZH                230 Winterthur        2022    115129
 2 1             ZH                261 Zürich            2022    423193
 3 2             BE                351 Bern              2020    134591
 4 12            BS               2701 Basel             2020    173232
 5 22            VD               5586 Lausanne          2021    140202
 6 25            GE               6621 Genève            2020    203951
 7 2             BE                371 Biel/Bienne       2020     55602
 8 3             LU               1061 Luzern            2020     82257
 9 17            SG               3203 St. Gallen        2020     76090
10 21            TI               5192 Lugano            2021     62315
11 1             ZH                 53 Bülach            2022     22193
12 1             ZH                 62 Kloten            2022     20779
13 1             ZH                 66 Opfikon           2022     21066
14 1             ZH                121 Wetzikon (ZH)     2022     25470
15 1             ZH                191 Dübendorf         2022     30568
16 1             ZH                198 Uster             2022     35529
17 1             ZH                243 Dietikon          2022     28126
18 1             ZH                293 Wädenswil         2022     24952
# ℹ 13 more variables: `FDP 1_Frauen` <chr>, `FDP 1_Männer` <chr>,
#   `FDP 1_Total` <chr>, `Die Mitte 2_Frauen` <chr>,
#   `Die Mitte 2_Männer` <chr>, `Die Mitte 2_Total` <chr>,
#   `CVP 2_Frauen` <chr>, `CVP 2_Männer` <chr>, `CVP 2_Total` <chr>,
#   SP_Frauen <chr>, SP_Männer <chr>, SP_Total <chr>,
#   Gemeindegrösse_Cluster <chr>
Code anzeigen
                                    # ncol() von df daten_wide --> Anz. Spalten
                                    # Anzahl Spalten = Position der letzten Spalte


daten_long <- daten_wide %>%
  mutate(across(matches("(_Frauen|_Männer|_Total)$"),
                ~ suppressWarnings(as.numeric(.)))) %>%       # Werte numerisch!
           pivot_longer(
             cols = matches("(_Frauen|_Männer|_Total)$"),     # Spaltenauswahl
             names_to = c("Partei", "m_w_Total"),             # Neue Label-Spalte
             names_pattern = "^(.*)_(Frauen|Männer|Total)$",  # RegEx 
             values_to = "Wert")                              # Neue Wert-Spalte


# Ansicht (Snapshot) nach transponieren & auffüllen
daten_long[1:8,
           1:ncol(daten_long)]
# A tibble: 8 × 10
  `Kantons-Nr.` Kanton `Gemeinde-Nr.` Gemeinde   Wahljahr Einwohner
  <chr>         <chr>           <dbl> <chr>         <dbl>     <dbl>
1 1             ZH                230 Winterthur     2022    115129
2 1             ZH                230 Winterthur     2022    115129
3 1             ZH                230 Winterthur     2022    115129
4 1             ZH                230 Winterthur     2022    115129
5 1             ZH                230 Winterthur     2022    115129
6 1             ZH                230 Winterthur     2022    115129
7 1             ZH                230 Winterthur     2022    115129
8 1             ZH                230 Winterthur     2022    115129
# ℹ 4 more variables: Gemeindegrösse_Cluster <chr>, Partei <chr>,
#   m_w_Total <chr>, Wert <dbl>

Dateipfad und gewünschte Sheets festlegen

Code anzeigen
dateipfad <- "~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.07.01_GEMEINDE_Die Exekutiven der statistischen Städte.xlsx"

selected_sheets <- c("2024", "2023", "2022","2021","2020","2019") 

Function für den Import der Gemeindedaten

Code anzeigen
importiere_sheet <- function(dateipfad, sheetname) {

      ###################################################################
      ########## DATEN IMPORTIEREN ######################################
      ########## Header definieren ######################################
      ###################################################################
      
      
      # Headerzeilen einlesen
      header <- suppressMessages(read_excel(dateipfad,
                   sheet = sheetname,
                   skip = 2,
                   n_max = 2,
                   col_names = FALSE))
      
      # Daten  einlesen
      daten_raw <- suppressMessages(read_excel(dateipfad,
                              sheet = sheetname,
                              skip = 5,
                              col_names = FALSE))
      
      
      # Die Spaltennamen zusammensetzen
      # Leere Zellen in 1. Headerzeile mit Werten von rechts auffüllen wenn leer/NA
      header_filled <- as.data.frame(t(header))   # t() "matrix transpose"
      header_filled <- fill(header_filled,        
                            V1,                   # Header Spalte 1 (V1) auffüllen
                            .direction = "down")  # Werte nach unten übernehmen
      header_filled <- t(header_filled)           # t() nochmals (zurück)
      
      # Spaltennamen kombinieren
      spaltennamen <- paste(header_filled[1, ],
                            header_filled[2, ],
                            sep = "_")
      spaltennamen <- gsub("_NA|NA_","", spaltennamen) # Entfernt überflüssige NAs
      
      # Spaltennamen zuweisen
      colnames(daten_raw) <- spaltennamen
      
      
      ###################################################################
      ########## TRANSPONIEREN ##########################################
      ########## Gemeindegrössen Cluster ################################
      ###################################################################
      
      
      # Cluster Einwohnerzahl als Spalte verwenden
      daten_wide <- daten_raw %>%
        mutate(Gemeindegrösse_Cluster =             # Name der neuen Spalte
                 ifelse(
                   is.na(                           # 4) auf NA prüfen
                     suppressWarnings(        # 3) Warnung aus (z.B. "≥ ..Einw..")
                     as.numeric(                    # 2) Wert als Zahl 
                     gsub("'", "", `Kantons-Nr.`)   # 1) Tausender Zeichen (')                                                              entfernen
                   ))),
                   as.character(`Kantons-Nr.`),     # 5) Wert von "Kantons-Nr" 
                   NA_character_                    #    sonst NA   
                 ))  %>%
        fill(Gemeindegrösse_Cluster,
             .direction = "down") %>%              # Cluster nach unten auffüllen
        filter(!is.na(suppressWarnings(
          as.numeric(gsub("'", "", `Kantons-Nr.`))))) # Herausfiltern von Zeilen                                                           mit NICHT-numerischem Wert
    
    
    # Ansicht (Snapshot) nach transponieren
    daten_wide[1:10,                    # 10 Zeilen
               c(1:10,                  # 10 Spalten +
                 ncol(daten_wide))]     # Letzte Spalte
                                        # ncol() von df daten_wide --> Anz. Spalten
                                        # Anzahl Spalten = Position letzte Spalte
    
    
    
    ###################################################################
    ########## TRANSPONIEREN ##########################################
    ########## Frauen, Männer, Total & Parteien ## ####################
    ###################################################################
    
    
    
    daten_long <- daten_wide %>%
      mutate(across(matches("(_Frauen|_Männer|_Total)$"),
                    ~ suppressWarnings(as.numeric(.)))) %>%     # Werte numerisch
      pivot_longer(cols = matches("(_Frauen|_Männer|_Total)$"), # Spaltenauswahl
                   names_to = c("Partei", "m_w_Total"),         # Neue Label-Spalte
                   names_pattern = "^(.*)_(Frauen|Männer|Total)$",  # RegEx
                   values_to = "Wert")                          # Neue Wert-Spalte
    
    return(daten_long)

}

Function ausführen: Import der Dateien testen

Code anzeigen
for (sheet in selected_sheets) {
  cat("\n-----------------------------\n")
  cat("Versuche Sheet:", sheet, "\n")
  result <- tryCatch({
    dat <- importiere_sheet(dateipfad, sheet)
    cat("Sheet erfolgreich eingelesen:", sheet, "\n")
    # Zusammenfassung:
    cat("Anzahl Zeilen:", nrow(dat), "\n")
    cat("Anzahl Spalten:", ncol(dat), "\n")
    cat("Spaltennamen (erste 15):", paste(names(dat)[1:min(15, ncol(dat))], collapse = ", "), "\n")
    TRUE
  }, error = function(e) {
    cat("Fehler beim Einlesen von Sheet:", sheet, "\n")
    cat("Fehlermeldung:", e$message, "\n")
    FALSE
  })
}

-----------------------------
Versuche Sheet: 2024 
Sheet erfolgreich eingelesen: 2024 
Anzahl Zeilen: 7515 
Anzahl Spalten: 10 
Spaltennamen (erste 15): Kantons-Nr., Kanton, Gemeinde-Nr., Gemeinde, Wahljahr, Einwohner, Gemeindegrösse_Cluster, Partei, m_w_Total, Wert 

-----------------------------
Versuche Sheet: 2023 
Sheet erfolgreich eingelesen: 2023 
Anzahl Zeilen: 7776 
Anzahl Spalten: 10 
Spaltennamen (erste 15): Kantons-Nr., Kanton, Gemeinde-Nr., Gemeinde, Wahljahr, Einwohner, Gemeindegrösse_Cluster, Partei, m_w_Total, Wert 

-----------------------------
Versuche Sheet: 2022 
Sheet erfolgreich eingelesen: 2022 
Anzahl Zeilen: 7776 
Anzahl Spalten: 10 
Spaltennamen (erste 15): Kantons-Nr., Kanton, Gemeinde-Nr., Gemeinde, Wahljahr, Einwohner, Gemeindegrösse_Cluster, Partei, m_w_Total, Wert 

-----------------------------
Versuche Sheet: 2021 
Sheet erfolgreich eingelesen: 2021 
Anzahl Zeilen: 7776 
Anzahl Spalten: 10 
Spaltennamen (erste 15): Kantons-Nr., Kanton, Gemeinde-Nr., Gemeinde, Wahljahr, Einwohner, Gemeindegrösse_Cluster, Partei, m_w_Total, Wert 

-----------------------------
Versuche Sheet: 2020 
Sheet erfolgreich eingelesen: 2020 
Anzahl Zeilen: 7290 
Anzahl Spalten: 10 
Spaltennamen (erste 15): Kantons-Nr., Kanton, Gemeinde-Nr., Gemeinde, Wahljahr, Einwohner, Gemeindegrösse_Cluster, Partei, m_w_Total, Wert 

-----------------------------
Versuche Sheet: 2019 
Sheet erfolgreich eingelesen: 2019 
Anzahl Zeilen: 14580 
Anzahl Spalten: 11 
Spaltennamen (erste 15): Kantons-Nr., Kanton, Gemeinde-Nr., Gemeinde, Einwohner, Grössenklasse-Nr., Grössenklasse, Gemeindegrösse_Cluster, Partei, m_w_Total, Wert 

Function ausführen: Import und Konsolidation

Code anzeigen
# Objekt erstellen, mit Sheets als tibble/dataframe
daten_liste <- lapply(selected_sheets,
                      function(sheet) {
                        importiere_sheet(dateipfad, sheet)})
str(daten_liste)
List of 6
 $ : tibble [7,515 × 10] (S3: tbl_df/tbl/data.frame)
  ..$ Kantons-Nr.           : chr [1:7515] "1" "1" "1" "1" ...
  ..$ Kanton                : chr [1:7515] "ZH" "ZH" "ZH" "ZH" ...
  ..$ Gemeinde-Nr.          : num [1:7515] 230 230 230 230 230 230 230 230 230 230 ...
  ..$ Gemeinde              : chr [1:7515] "Winterthur" "Winterthur" "Winterthur" "Winterthur" ...
  ..$ Wahljahr              : num [1:7515] 2022 2022 2022 2022 2022 ...
  ..$ Einwohner             : num [1:7515] 115129 115129 115129 115129 115129 ...
  ..$ Gemeindegrösse_Cluster: chr [1:7515] "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" ...
  ..$ Partei                : chr [1:7515] "FDP 1" "FDP 1" "FDP 1" "Die Mitte 2" ...
  ..$ m_w_Total             : chr [1:7515] "Frauen" "Männer" "Total" "Frauen" ...
  ..$ Wert                  : num [1:7515] 0 1 1 0 1 1 NA NA NA 1 ...
 $ : tibble [7,776 × 10] (S3: tbl_df/tbl/data.frame)
  ..$ Kantons-Nr.           : chr [1:7776] "1" "1" "1" "1" ...
  ..$ Kanton                : chr [1:7776] "ZH" "ZH" "ZH" "ZH" ...
  ..$ Gemeinde-Nr.          : num [1:7776] 230 230 230 230 230 230 230 230 230 230 ...
  ..$ Gemeinde              : chr [1:7776] "Winterthur" "Winterthur" "Winterthur" "Winterthur" ...
  ..$ Wahljahr              : num [1:7776] 2022 2022 2022 2022 2022 ...
  ..$ Einwohner             : num [1:7776] 115129 115129 115129 115129 115129 ...
  ..$ Gemeindegrösse_Cluster: chr [1:7776] "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" ...
  ..$ Partei                : chr [1:7776] "FDP 1" "FDP 1" "FDP 1" "Die Mitte 2" ...
  ..$ m_w_Total             : chr [1:7776] "Frauen" "Männer" "Total" "Frauen" ...
  ..$ Wert                  : num [1:7776] 0 1 1 0 1 1 NA NA NA 1 ...
 $ : tibble [7,776 × 10] (S3: tbl_df/tbl/data.frame)
  ..$ Kantons-Nr.           : chr [1:7776] "1" "1" "1" "1" ...
  ..$ Kanton                : chr [1:7776] "ZH" "ZH" "ZH" "ZH" ...
  ..$ Gemeinde-Nr.          : num [1:7776] 230 230 230 230 230 230 230 230 230 230 ...
  ..$ Gemeinde              : chr [1:7776] "Winterthur" "Winterthur" "Winterthur" "Winterthur" ...
  ..$ Wahljahr              : num [1:7776] 2022 2022 2022 2022 2022 ...
  ..$ Einwohner             : num [1:7776] 115129 115129 115129 115129 115129 ...
  ..$ Gemeindegrösse_Cluster: chr [1:7776] "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" ...
  ..$ Partei                : chr [1:7776] "FDP 1" "FDP 1" "FDP 1" "Die Mitte 2" ...
  ..$ m_w_Total             : chr [1:7776] "Frauen" "Männer" "Total" "Frauen" ...
  ..$ Wert                  : num [1:7776] 0 1 1 0 1 1 NA NA NA 1 ...
 $ : tibble [7,776 × 10] (S3: tbl_df/tbl/data.frame)
  ..$ Kantons-Nr.           : chr [1:7776] "1" "1" "1" "1" ...
  ..$ Kanton                : chr [1:7776] "ZH" "ZH" "ZH" "ZH" ...
  ..$ Gemeinde-Nr.          : num [1:7776] 230 230 230 230 230 230 230 230 230 230 ...
  ..$ Gemeinde              : chr [1:7776] "Winterthur" "Winterthur" "Winterthur" "Winterthur" ...
  ..$ Wahljahr              : num [1:7776] 2018 2018 2018 2018 2018 ...
  ..$ Einwohner             : num [1:7776] 110912 110912 110912 110912 110912 ...
  ..$ Gemeindegrösse_Cluster: chr [1:7776] "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" ...
  ..$ Partei                : chr [1:7776] "FDP 1" "FDP 1" "FDP 1" "Die Mitte 2" ...
  ..$ m_w_Total             : chr [1:7776] "Frauen" "Männer" "Total" "Frauen" ...
  ..$ Wert                  : num [1:7776] 1 1 2 NA NA NA 0 1 1 2 ...
 $ : tibble [7,290 × 10] (S3: tbl_df/tbl/data.frame)
  ..$ Kantons-Nr.           : chr [1:7290] "1" "1" "1" "1" ...
  ..$ Kanton                : chr [1:7290] "ZH" "ZH" "ZH" "ZH" ...
  ..$ Gemeinde-Nr.          : num [1:7290] 230 230 230 230 230 230 230 230 230 230 ...
  ..$ Gemeinde              : chr [1:7290] "Winterthur" "Winterthur" "Winterthur" "Winterthur" ...
  ..$ Wahljahr              : num [1:7290] 2018 2018 2018 2018 2018 ...
  ..$ Einwohner             : num [1:7290] 110912 110912 110912 110912 110912 ...
  ..$ Gemeindegrösse_Cluster: chr [1:7290] "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" ...
  ..$ Partei                : chr [1:7290] "FDP 1)" "FDP 1)" "FDP 1)" "CVP" ...
  ..$ m_w_Total             : chr [1:7290] "Frauen" "Männer" "Total" "Frauen" ...
  ..$ Wert                  : num [1:7290] 1 1 2 0 1 1 2 1 3 NA ...
 $ : tibble [14,580 × 11] (S3: tbl_df/tbl/data.frame)
  ..$ Kantons-Nr.           : chr [1:14580] "1" "1" "1" "1" ...
  ..$ Kanton                : chr [1:14580] "ZH" "ZH" "ZH" "ZH" ...
  ..$ Gemeinde-Nr.          : num [1:14580] 230 230 230 230 230 230 230 230 230 230 ...
  ..$ Gemeinde              : chr [1:14580] "Winterthur" "Winterthur" "Winterthur" "Winterthur" ...
  ..$ Einwohner             : num [1:14580] 111851 111851 111851 111851 111851 ...
  ..$ Grössenklasse-Nr.     : chr [1:14580] "4" "4" "4" "4" ...
  ..$ Grössenklasse         : chr [1:14580] "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" ...
  ..$ Gemeindegrösse_Cluster: chr [1:14580] "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" ...
  ..$ Partei                : chr [1:14580] "FDP 1)" "FDP 1)" "FDP 1)" "CVP" ...
  ..$ m_w_Total             : chr [1:14580] "Frauen" "Männer" "Total" "Frauen" ...
  ..$ Wert                  : num [1:14580] 1 1 2 0 1 1 2 1 3 NA ...
Code anzeigen
lapply(daten_liste, summary)
[[1]]
 Kantons-Nr.           Kanton           Gemeinde-Nr.    Gemeinde        
 Length:7515        Length:7515        Min.   :   2   Length:7515       
 Class :character   Class :character   1st Qu.: 581   Class :character  
 Mode  :character   Mode  :character   Median :2769   Mode  :character  
                                       Mean   :2936                     
                                       3rd Qu.:5113                     
                                       Max.   :6711                     
                                                                        
    Wahljahr      Einwohner      Gemeindegrösse_Cluster    Partei         
 Min.   :2020   Min.   :  4957   Length:7515            Length:7515       
 1st Qu.:2021   1st Qu.: 11680   Class :character       Class :character  
 Median :2022   Median : 15763   Mode  :character       Mode  :character  
 Mean   :2022   Mean   : 25286                                            
 3rd Qu.:2024   3rd Qu.: 22110                                            
 Max.   :2024   Max.   :423193                                            
                                                                          
  m_w_Total              Wert      
 Length:7515        Min.   : 0.00  
 Class :character   1st Qu.: 1.00  
 Mode  :character   Median : 1.00  
                    Mean   : 1.73  
                    3rd Qu.: 2.00  
                    Max.   :30.00  
                    NA's   :4986   

[[2]]
 Kantons-Nr.           Kanton           Gemeinde-Nr.    Gemeinde        
 Length:7776        Length:7776        Min.   :   2   Length:7776       
 Class :character   Class :character   1st Qu.: 546   Class :character  
 Mode  :character   Mode  :character   Median :2770   Mode  :character  
                                       Mean   :2952                     
                                       3rd Qu.:5192                     
                                       Max.   :6711                     
                                                                        
    Wahljahr      Einwohner      Gemeindegrösse_Cluster    Partei         
 Min.   :2020   Min.   :  4957   Length:7776            Length:7776       
 1st Qu.:2020   1st Qu.: 12104   Class :character       Class :character  
 Median :2021   Median : 15754   Mode  :character       Mode  :character  
 Mean   :2021   Mean   : 25369                                            
 3rd Qu.:2022   3rd Qu.: 22193                                            
 Max.   :2023   Max.   :423193                                            
                                                                          
  m_w_Total              Wert       
 Length:7776        Min.   : 0.000  
 Class :character   1st Qu.: 1.000  
 Mode  :character   Median : 1.000  
                    Mean   : 1.719  
                    3rd Qu.: 2.000  
                    Max.   :30.000  
                    NA's   :5316    

[[3]]
 Kantons-Nr.           Kanton           Gemeinde-Nr.    Gemeinde        
 Length:7776        Length:7776        Min.   :   2   Length:7776       
 Class :character   Class :character   1st Qu.: 546   Class :character  
 Mode  :character   Mode  :character   Median :2770   Mode  :character  
                                       Mean   :2952                     
                                       3rd Qu.:5192                     
                                       Max.   :6711                     
                                                                        
    Wahljahr      Einwohner      Gemeindegrösse_Cluster    Partei         
 Min.   :2019   Min.   :  4957   Length:7776            Length:7776       
 1st Qu.:2020   1st Qu.: 12104   Class :character       Class :character  
 Median :2021   Median : 15754   Mode  :character       Mode  :character  
 Mean   :2021   Mean   : 25353                                            
 3rd Qu.:2022   3rd Qu.: 22193                                            
 Max.   :2022   Max.   :423193                                            
                                                                          
  m_w_Total              Wert       
 Length:7776        Min.   : 0.000  
 Class :character   1st Qu.: 1.000  
 Mode  :character   Median : 1.000  
                    Mean   : 1.725  
                    3rd Qu.: 2.000  
                    Max.   :30.000  
                    NA's   :5325    

[[4]]
 Kantons-Nr.           Kanton           Gemeinde-Nr.    Gemeinde        
 Length:7776        Length:7776        Min.   :   2   Length:7776       
 Class :character   Class :character   1st Qu.: 546   Class :character  
 Mode  :character   Mode  :character   Median :2770   Mode  :character  
                                       Mean   :2952                     
                                       3rd Qu.:5192                     
                                       Max.   :6711                     
                                                                        
    Wahljahr      Einwohner      Gemeindegrösse_Cluster    Partei         
 Min.   :2017   Min.   :  4994   Length:7776            Length:7776       
 1st Qu.:2018   1st Qu.: 12068   Class :character       Class :character  
 Median :2020   Median : 15754   Mode  :character       Mode  :character  
 Mean   :2020   Mean   : 25043                                            
 3rd Qu.:2021   3rd Qu.: 21718                                            
 Max.   :2021   Max.   :409241                                            
                                                                          
  m_w_Total              Wert       
 Length:7776        Min.   : 0.000  
 Class :character   1st Qu.: 1.000  
 Mode  :character   Median : 1.000  
                    Mean   : 1.722  
                    3rd Qu.: 2.000  
                    Max.   :30.000  
                    NA's   :5316    

[[5]]
 Kantons-Nr.           Kanton           Gemeinde-Nr.    Gemeinde        
 Length:7290        Length:7290        Min.   :   2   Length:7290       
 Class :character   Class :character   1st Qu.: 546   Class :character  
 Mode  :character   Mode  :character   Median :2770   Mode  :character  
                                       Mean   :2951                     
                                       3rd Qu.:5192                     
                                       Max.   :6711                     
                                                                        
    Wahljahr      Einwohner      Gemeindegrösse_Cluster    Partei         
 Min.   :2016   Min.   :  4994   Length:7290            Length:7290       
 1st Qu.:2017   1st Qu.: 11572   Class :character       Class :character  
 Median :2018   Median : 15710   Mode  :character       Mode  :character  
 Mean   :2018   Mean   : 24871                                            
 3rd Qu.:2020   3rd Qu.: 21090                                            
 Max.   :2020   Max.   :409241                                            
                                                                          
  m_w_Total              Wert       
 Length:7290        Min.   : 0.000  
 Class :character   1st Qu.: 1.000  
 Mode  :character   Median : 1.000  
                    Mean   : 1.717  
                    3rd Qu.: 2.000  
                    Max.   :30.000  
                    NA's   :4827    

[[6]]
 Kantons-Nr.           Kanton           Gemeinde-Nr.    Gemeinde        
 Length:14580       Length:14580       Min.   :   2   Length:14580      
 Class :character   Class :character   1st Qu.: 546   Class :character  
 Mode  :character   Mode  :character   Median :2770   Mode  :character  
                                       Mean   :2951                     
                                       3rd Qu.:5192                     
                                       Max.   :6711                     
                                                                        
   Einwohner      Grössenklasse-Nr.  Grössenklasse      Gemeindegrösse_Cluster
 Min.   :  4928   Length:14580       Length:14580       Length:14580          
 1st Qu.: 11733   Class :character   Class :character   Class :character      
 Median : 15693   Mode  :character   Mode  :character   Mode  :character      
 Mean   : 25006                                                               
 3rd Qu.: 21506                                                               
 Max.   :415367                                                               
                                                                              
    Partei           m_w_Total              Wert      
 Length:14580       Length:14580       Min.   : 0.00  
 Class :character   Class :character   1st Qu.: 0.00  
 Mode  :character   Mode  :character   Median : 0.00  
                                       Mean   : 0.65  
                                       3rd Qu.: 1.00  
                                       Max.   :30.00  
                                       NA's   :8084   
Code anzeigen
# Alle tibbles zu einem Dataframe zusammenfügen
elec_gemeinde_exekutiven_final <- bind_rows(daten_liste, .id = "Sheet")

2.4 Parteilandschaft

Code anzeigen
parties_rating <- read_excel("~/CAS/Zertifikatsarbeit/data/parties_economic_socio-political_rating.xlsx",
                             sheet = "matrix_eco-socio_enhanced")

3 Transformation & visualization

3.1 Abstimmungen

Code anzeigen
voting <- voting_raw %>%
  mutate(datum = as.Date(datum, format = "%d.%m.%Y")) %>%  # Format-Umwandlung
  filter(datum >= as.Date("2019-12-31")) %>%               # Vergleich
  select(anr,                                              # Spalten-Wahl
         datum,
         titel_kurz_d,
         titel_off_d,
         rechtsform, #1 Obligatorisches Referendum
                     #2 Fakultatives Referendum
                     #3 Volksinitiative
                     #4 Direkter Gegenentwurf zu einer Volksinitiative
                     #5 Stichfrage
         dep,
         `br-pos`,   #1 Befürwortend
                     #2 Ablehnend
                     #3 Keine
                     #8 Vorzug für den Gegenentwurf (bei Stichfragen)
                     #9 Vorzug für die Volksinitiative (bei Stichfragen)
                     #. Missing
         legisjahr,
         `pa-iv`,
         `bv-pos`,   #1 Befürwortend
                     #2 Ablehnend
                     #3 Keine Abstimmungsempfehlung des Parlaments
                     #8 Vorzug für den Gegenentwurf (bei Stichfragen)
         `nr-pos`,   #1 Befürwortende Mehrheit im Nationalrat
                     #2 Ablehnende Mehrheit im Nationalrat
                     #3 Keine Abstimmungsempfehlung des Nationalrats
                     #8 Vorzug für den Gegenentwurf (bei Stichfragen)
         nrja,
         nrnein,
         `sr-pos`,   #1 Befürwortende Mehrheit im Ständerat
                     #2 Ablehnende Mehrheit im Ständerat
                     #3 Keine Abstimmungsempfehlung des Ständerats
                     #8 Vorzug für den Gegenentwurf (bei Stichfragen)
         srja,
         srnein,
         starts_with("p-"),
                     #1 Ja-Parole
                     #2 Nein-Parole
                     #3 keine Parole abzugeben
                     #4 empfahl, einen leeren Stimmzettel einzulegen
                     #5 Stimmfreigabe
                     #8 Bevorzugung des Gegenentwurfs (bei Stichfrage)
                     #9 Bevorzugung der Volksinitiative (bei Stichfrage)
                     #66 Neutral: keine Parole oder Empfehlung auf leer einlegen                        #9999 Organisation existiert nicht
                     #. Unbekannt
         volk,       #0 Eine Mehrheit der Abstimmenden hat die Vorlage abgelehnt
                     #1 Eine Mehrheit der Abstimmenden hat die Vorlage angenommen
                     #8 Bei Stichfragen:  Mehrheit für Gegenentwurfs
                     #9 Bei Stichfragen:  Mehrheit Volksinitiative
         stand,
                     #0 Die Vorlage hat keine Mehrheit der Standesstimmen er-reicht
                     #1 Die Vorlage hat die Mehrheit der Standesstimmen erreicht
                     #3 Ständemehr nicht notwendig
                     #8 Bei Stichfragen: Mehrheit für Gegenentwurf
                     #9 Bei Stichfragen: Mehrheit für Volksinitiative
         annahme,    #0 Ablehnung der Vorlage
                     #1 Annahme der Vorlage
                     #8 Bei Stichfragen: Gegenentwurf angenommen
                     #9 Bei Stichfragen: Volksinitiative angenommen
                     #. Bei Stichfragen: Ergebnis der Stichfrage obsolet
         berecht,
         stimmen,
         bet,
         leer,
         ungultig,
         gultig,
         volkja,
         volknein,
         `volkja-proz`)
Code anzeigen
grep("^p-", names(voting), value = TRUE)
 [1] "p-fdp"                "p-sps"                "p-svp"               
 [4] "p-mitte"              "p-evp"                "p-gps"               
 [7] "p-glp"                "p-pda"                "p-sd"                
[10] "p-edu"                "p-fps"                "p-lega"              
[13] "p-kvp"                "p-mcg"                "p-ucsp"              
[16] "p-cvp"                "p-bdp"                "p-lps"               
[19] "p-ldu"                "p-poch"               "p-rep"               
[22] "p-eco"                "p-sgv"                "p-sbv"               
[25] "p-sgb"                "p-travs"              "p-sav"               
[28] "p-vsa"                "p-vpod"               "p-voev"              
[31] "p-tcs"                "p-vcs"                "p-acs"               
[34] "p-sbk"                "p-ssv"                "p-gem"               
[37] "p-kdk"                "p-kkjpd"              "p-gdk"               
[40] "p-ldk"                "p-vdk"                "p-sodk"              
[43] "p-endk"               "p-fdk"                "p-edk"               
[46] "p-bpuk"               "p-others_yes"         "p-others_yes-fr"     
[49] "p-others_no"          "p-others_no-fr"       "p-others_free"       
[52] "p-others_free-fr"     "p-others_counterp"    "p-others_counterp-fr"
[55] "p-others_init"        "p-others_init-fr"    
Code anzeigen
paste(grep("^p-", names(voting), value = TRUE), collapse = ", ")
[1] "p-fdp, p-sps, p-svp, p-mitte, p-evp, p-gps, p-glp, p-pda, p-sd, p-edu, p-fps, p-lega, p-kvp, p-mcg, p-ucsp, p-cvp, p-bdp, p-lps, p-ldu, p-poch, p-rep, p-eco, p-sgv, p-sbv, p-sgb, p-travs, p-sav, p-vsa, p-vpod, p-voev, p-tcs, p-vcs, p-acs, p-sbk, p-ssv, p-gem, p-kdk, p-kkjpd, p-gdk, p-ldk, p-vdk, p-sodk, p-endk, p-fdk, p-edk, p-bpuk, p-others_yes, p-others_yes-fr, p-others_no, p-others_no-fr, p-others_free, p-others_free-fr, p-others_counterp, p-others_counterp-fr, p-others_init, p-others_init-fr"

3.2 Wahlen

3.2.1 Bundesebene

3.2.1.1 Nationalrat

Code anzeigen
#TODO Es kann innerhalb der Legislatur Wechsel geben --> Anstelle der heute "Aktiven" ist es deshalb genauer die Räte/Konstellation zum Zeitpunkt der jeweiligen Abstimmung zu prüfen. 

# Anzeige mit datatable()
datatable(elec_nationalrat,
          class = 'nowrap',
          filter = 'top',
          options = list(pageLength = 7,
                         scrollX = TRUE,
                         search = list(regex = TRUE,
                                       caseInsensitive = TRUE)))
Code anzeigen
# Fehlende Werte (NA), Klassen und Levels prüfen
Abstract(elec_nationalrat)
────────────────────────────────────────────────────────────────────────────── 
elec_nationalrat

data frame: 492 obs. of  21 variables
        0 complete cases (0.0%)

  Nr  Class  ColName                NAs           Levels
  1   log    Active                   .                 
  2   chr    FirstName                .                 
  3   chr    LastName                 .                 
  4   chr    GenderAsString           .                 
  5   chr    CantonName               .                 
  6   chr    CantonAbbreviation       .                 
  7   chr    CouncilName              .                 
  8   chr    ParlGroupName            2 (0.4%)          
  9   chr    ParlGroupAbbreviation    2 (0.4%)          
  10  chr    PartyName                .                 
  11  chr    PartyAbbreviation        .                 
  12  chr    MaritalStatusText      324 (65.9%)         
  13  chr    Nationality              1 (0.2%)          
  14  chr    BirthPlace_City          2 (0.4%)          
  15  chr    BirthPlace_Canton       17 (3.5%)          
  16  chr    Mandates                47 (9.6%)          
  17  chr    DateJoining              .                 
  18  chr    DateLeaving            204 (41.5%)         
  19  chr    Citizenship              4 (0.8%)          
  20  chr    DateOfBirth              .                 
  21  log    DateOfDeath            492 (100.0%)        
Code anzeigen
PlotMiss(elec_nationalrat)

Code anzeigen
# Datensatz reduzieren auf Aktive und doppelte Einträge elimnieren
elec_nationalrat_reduced <- elec_nationalrat %>%
  filter(Active == TRUE) %>% 
  distinct(.keep_all = TRUE) %>% 
  select(CantonAbbreviation,
         CantonName,
         PartyAbbreviation,
         PartyName) %>% 
  mutate(value = 1)

datatable(elec_nationalrat_reduced,
          class = 'nowrap',
          filter = 'top',
          options = list(pageLength = 7,
                         scrollX = TRUE,
                         search = list(regex = TRUE,
                                       caseInsensitive = TRUE)))
Code anzeigen
# Sitze pro Kanton, Partei und Jahr 
    
    # long format
      df_sum <- elec_nationalrat_reduced %>%
      group_by(CantonAbbreviation, PartyAbbreviation) %>%
      summarise(sum_value = sum(value, na.rm = TRUE),
                .groups = "drop")
    
    # wide format
    # Pivotieren: Partei-Spalten erzeugen
    df_wide <- df_sum %>%
      pivot_wider(names_from = PartyAbbreviation,
                  values_from = sum_value,
                  values_fill = 0)
    
    
    
    # Partei-Spaltennamen extrahieren (ohne Kanton/Jahr)
    partei_cols <- setdiff(names(df_wide),
                           c("CantonAbbreviation"))
    
    # Spaltensummen der Parteien berechnen für Sortierung
    partei_sums <- colSums(df_wide[partei_cols])
    
    # Zeilensumme der Kantone/Jahre hinzufügen
    df_wide$Total <- rowSums(df_wide[partei_cols])
    
    
    # Parteispalten-Anordnung nach Summe sortieren
    sorted_partei <- names(sort(partei_sums,
                                decreasing = TRUE))
    
    
    # Dataframe neu anordnen: Kanton/Jahr,  sortierte und gefilterte Parteien
    elec_nationalrat_final <- df_wide[, c(setdiff(names(df_wide),
                                                  partei_cols),
                                          sorted_partei)] %>% 
      arrange(desc(Total))
    
datatable(elec_nationalrat_final,
          class = 'nowrap',
          filter = 'top',
          options = list(pageLength = 7,
                         scrollX = TRUE,
                         search = list(regex = TRUE,
                                       caseInsensitive = TRUE)))

3.2.1.2 Ständerat

Code anzeigen
#TODO Es kann innerhalb der Legislatur Wechsel geben --> Anstelle der heute "Aktiven" ist es deshalb genauer die Räte/Konstellation zum Zeitpunkt der jeweiligen Abstimmung zu prüfen.


# Anzeige mit datatable()
datatable(elec_ständerat,
          class = 'nowrap',
          filter = 'top',
          options = list(pageLength = 7,
                         scrollX = TRUE,
                         search = list(regex = TRUE,
                                       caseInsensitive = TRUE)))
Code anzeigen
# Fehlende Werte (NA), Klassen und Levels prüfen
Abstract(elec_ständerat)
────────────────────────────────────────────────────────────────────────────── 
elec_ständerat

data frame: 155 obs. of  21 variables
        0 complete cases (0.0%)

  Nr  Class  ColName                NAs           Levels
  1   log    Active                   .                 
  2   chr    FirstName                .                 
  3   chr    LastName                 .                 
  4   chr    GenderAsString           .                 
  5   chr    CantonName               .                 
  6   chr    CantonAbbreviation       .                 
  7   chr    CouncilName              .                 
  8   chr    ParlGroupName            .                 
  9   chr    ParlGroupAbbreviation    .                 
  10  chr    PartyName                .                 
  11  chr    PartyAbbreviation        .                 
  12  chr    MaritalStatusText      116 (74.8%)         
  13  chr    Nationality              .                 
  14  chr    BirthPlace_City          .                 
  15  chr    BirthPlace_Canton        .                 
  16  chr    Mandates                11 (7.1%)          
  17  chr    DateJoining              .                 
  18  chr    DateLeaving             45 (29.0%)         
  19  chr    Citizenship              .                 
  20  chr    DateOfBirth              .                 
  21  log    DateOfDeath            155 (100.0%)        
Code anzeigen
PlotMiss(elec_ständerat)

Code anzeigen
# Datensatz reduzieren auf Aktive und doppelte Einträge elimnieren
elec_ständerat_reduced <- elec_ständerat %>%
  filter(Active == TRUE) %>% 
  distinct(.keep_all = TRUE) %>% 
  select(CantonAbbreviation,
         CantonName,
         PartyAbbreviation,
         PartyName) %>% 
  mutate(value = 1)

datatable(elec_ständerat_reduced,
          class = 'nowrap',
          filter = 'top',
          options = list(pageLength = 7,
                         scrollX = TRUE,
                         search = list(regex = TRUE,
                                       caseInsensitive = TRUE)))
Code anzeigen
# Sitze pro Kanton, Partei und Jahr 
    
    # long format
      df_sum <- elec_ständerat_reduced %>%
      group_by(CantonAbbreviation, PartyAbbreviation) %>%
      summarise(sum_value = sum(value, na.rm = TRUE),
                .groups = "drop")
    
    # wide format
    # Pivotieren: Partei-Spalten erzeugen
    df_wide <- df_sum %>%
      pivot_wider(names_from = PartyAbbreviation,
                  values_from = sum_value,
                  values_fill = 0)
    
    
    # Partei-Spaltennamen extrahieren (ohne Kanton/Jahr)
    partei_cols <- setdiff(names(df_wide),
                           c("CantonAbbreviation"))
    
    # Spaltensummen der Parteien berechnen für Sortierung
    partei_sums <- colSums(df_wide[partei_cols])
    
    # Zeilensumme der Kantone/Jahre hinzufügen
    df_wide$Total <- rowSums(df_wide[partei_cols])
    
    
    # Parteispalten-Anordnung nach Summe sortieren
    sorted_partei <- names(sort(partei_sums,
                                decreasing = TRUE))
    
    
    # Dataframe neu anordnen: Kanton/Jahr,  sortierte und gefilterte Parteien
    elec_ständerat_final <- df_wide[, c(setdiff(names(df_wide),
                                                  partei_cols),
                                        sorted_partei)] %>% 
      arrange(desc(Total))
    
datatable(elec_ständerat_final,
          class = 'nowrap',
          filter = 'top',
          options = list(pageLength = 7,
                         scrollX = TRUE,
                         search = list(regex = TRUE,
                                       caseInsensitive = TRUE)))

3.2.2 Kantonsebene

3.2.2.1 Kantonsparlament

3.2.2.2 Kantonsregierung

Code anzeigen
datatable(elec_kantonsregierung_final,
         class = 'nowrap',
          filter = 'top',
          options = list(pageLength = 7,
                         scrollX = TRUE,
                         search = list(regex = TRUE,
                                       caseInsensitive = TRUE)))
Code anzeigen
# Änderungen verfolgen
# Partei-Spalten bestimmen
parteien <- setdiff(names(elec_kantonsregierung_final), c("Kanton", "Wahljahr", "Total"))

# Dataframe nach Kanton und Wahljahr sortieren und ins lange Format bringen
df_long <- elec_kantonsregierung_final %>%
  arrange(Kanton, Wahljahr) %>%
  pivot_longer(
    cols = all_of(parteien),
    names_to = "Partei",
    values_to = "Wert_neu"
  ) %>%
  group_by(Kanton, Partei) %>%
  arrange(Wahljahr, .by_group = TRUE) %>%
  mutate(Wert_alt = lag(Wert_neu)) %>%
  ungroup()

# Änderungen filtern: Zeige nur, wo sich der Wert geändert hat (und nicht NA)
elec_kantonsregierung_changelog <- df_long %>%
  filter(!is.na(Wert_alt),
         Wert_neu != Wert_alt) %>%
  select(Kanton,
         Wahljahr,
         Partei,
         Wert_alt,
         Wert_neu)


datatable(elec_kantonsregierung_changelog,
          class = 'nowrap',
          filter = 'top',
          options = list(pageLength = 7,
                         scrollX = TRUE,
                         search = list(regex = TRUE,
                                       caseInsensitive = TRUE)))

3.2.3 Gemeindeebene

3.2.3.1 Gemeinde

3.3 Parteilandschaft

3.3.1 Multi-Dimensions-Model

3.3.1.1 Minimalwerte

Für jede Partei den Minimalwert des Abstimmungsverhaltens zu verwenden, basiert auf der Zielsetzung, die klarste politische Position einer Partei in einem bestimmten Themenbereich zu identifizieren. Diese Methodik stellt sicher, dass auch bei wenigen Abweichungen von der Mehrheitslinie die tatsächliche Haltung der Partei deutlich erkennbar bleibt.

Code anzeigen
ggplot(parties_rating,
       aes(x = eco_pct_min_x,
           y = socio_pct_min_y,
           label = Partei)) +
  geom_point(size = 3) +
  geom_text(vjust = -0.8) +
  geom_segment(aes(x = 50, xend = 50,
                   y = 0, yend = 100), linetype = "dashed", color = "blue") +
  geom_segment(aes(x = 0, xend = 100,
                   y = 50, yend = 50), linetype = "dashed", color = "blue") +
  
  annotate("text",
           x = 0,
           y = min(parties_rating$socio_pct_min_y) + 55,
           label = "Links/staatsgläubig", size = 4, hjust = 0.5) +
  annotate("text",
           x = 100,
           y = min(parties_rating$socio_pct_min_y) + 55,
           label = "Rechts/marktwirtschaftlich", size = 4, hjust = 0.5) +
  annotate("text",
           x = 50,
           y = min(parties_rating$socio_pct_min_y) - 3,
           label = "Autoritär/Konservativ", size = 4, hjust = 0.5) +
  annotate("text",
           x = 50,
           y = min(parties_rating$socio_pct_min_y) + 103,
           label = "Libertär/Progressiv", size = 4, hjust = 0.5) +
  scale_x_continuous(limits = c(0, 100)) +
  labs(x = "Wirtschaftspolitisch",
       y = "Gesellschaftspolitisch",
       title = "Politische Positionierung im Zwei-Achsen-Modell",
       subtitle = "Grundlage: Minimalwerte des Abstimmungsverhaltens") +
  theme_minimal()+
  theme(plot.title = element_text(size = 18,
                                  margin = margin(b = 30)),
        axis.title.x = element_text(size = 14,
                                    margin = margin(t = 5)),
        axis.title.y = element_text(size = 14,
                                    margin = margin(r = 5))) +
  coord_cartesian(ylim = c(0, 105),
                  xlim = c(0, 105),
                  clip = "off")

3.3.1.2 Durchschnittswerte als Zentrum und Min/Max als Ellipse

Code anzeigen
ggplot(parties_rating,
       aes(x = eco_pct_avg_x,
           y = socio_pct_avg_y,
           label = Partei)) +
  geom_ellipse(
    aes(x0 = eco_pct_avg_x,
        y0 = socio_pct_avg_y,
        a = (eco_pct_max_x - eco_pct_min_x) / 2, # Halbachse x
        b = (socio_pct_max_y - socio_pct_min_y) / 2, # Halbachse y
        angle = 0),
    fill = "gray80", alpha = 0.4) +
  geom_point(size = 3) +
  geom_text(vjust = -0.8) +
  geom_segment(aes(x = 50, xend = 50,
                   y = 0, yend = 100), linetype = "dashed", color = "blue") +
  geom_segment(aes(x = 0, xend = 100,
                   y = 50, yend = 50), linetype = "dashed", color = "blue") +
  annotate("text",
           x = 0,
           y = min(parties_rating$socio_pct_min_y) + 55,
           label = "Links/staatsgläubig", size = 4, hjust = 0.5) +
  annotate("text",
           x = 100,
           y = min(parties_rating$socio_pct_min_y) + 55,
           label = "Rechts/marktwirtschaftlich", size = 4, hjust = 0.5) +
  annotate("text",
           x = 50,
           y = min(parties_rating$socio_pct_min_y) - 3,
           label = "Autoritär/Konservativ", size = 4, hjust = 0.5) +
  annotate("text",
           x = 50,
           y = min(parties_rating$socio_pct_min_y) + 103,
           label = "Libertär/Progressiv", size = 4, hjust = 0.5) +
  scale_x_continuous(limits = c(-10, +110)) +
  labs(x = "Wirtschaftspolitisch",
       y = "Gesellschaftspolitisch",
       title = "Politische Positionierung im Zwei-Achsen-Modell",
       subtitle = "Grundlage: Durchschnitt des Abstimmungsverhaltens als Zentrum, Min-/Max als Ellipse") +
  theme_minimal()+
  theme(plot.title = element_text(size = 18,
                                  margin = margin(b = 30)),
        axis.title.x = element_text(size = 14,
                                    margin = margin(t = 5)),
        axis.title.y = element_text(size = 14,
                                    margin = margin(r = 5))) +
  coord_cartesian(ylim = c(0, 100),
                  xlim = c(0, 100),
                  clip = "off")

3.3.1.3 Hauptparteien: Durchschnittswerte als Zentrum und Min/Max als Ellipse / Klein- und Regionalparteien nur mit ihrem Durchschnittswert

Code anzeigen
ggplot(parties_rating,
       aes(x = eco_pct_avg_x,
           y = socio_pct_avg_y,
           label = Partei)) +
  geom_ellipse(aes(x0 = eco_pct_avg_x,
                   y0 = socio_pct_avg_y,
                   a = (eco_pct_max_x - eco_pct_min_x) / 2, # Halbachse x
                   b = (socio_pct_max_y - socio_pct_min_y) / 2, # Halbachse y
                   angle = 0),
               fill = "gray80", alpha = 0.4) +
  geom_point(size = 3) +
  
  # Kleinere Parteien
  geom_point(data = subset(parties_rating,
                           `Grosse Partei` == "n" &
                             Relevanz_nationale_Abstimmungen != "-"),
             aes(x = eco_x,
                 y = socio_y),
             shape = 21, fill = "grey", color = "black", size = 1, stroke = 1) +
  
  # Labels der kleineren Partein
  geom_text(data = subset(parties_rating,
                          `Grosse Partei` == "n" &
                            Relevanz_nationale_Abstimmungen != "-"),
            aes(x = eco_x,
                y = socio_y,
                label = Partei),
            vjust = -1, fontface = "plain", color = "darkgrey") +
  geom_text(vjust = -0.8) +
  geom_segment(aes(x = 50, xend = 50,
                   y = 0, yend = 100), linetype = "dashed", color = "blue") +
  geom_segment(aes(x = 0, xend = 100,
                   y = 50, yend = 50), linetype = "dashed", color = "blue") +
  annotate("text",
           x = 0,
           y = min(parties_rating$socio_pct_min_y) + 55,
           label = "Links/staatsgläubig", size = 4, hjust = 0.5) +
  annotate("text",
           x = 100,
           y = min(parties_rating$socio_pct_min_y) + 55,
           label = "Rechts/marktwirtschaftlich", size = 4, hjust = 0.5) +
  annotate("text",
           x = 50,
           y = min(parties_rating$socio_pct_min_y) - 3,
           label = "Autoritär/Konservativ", size = 4, hjust = 0.5) +
  annotate("text",
           x = 50,
           y = min(parties_rating$socio_pct_min_y) + 103,
           label = "Libertär/Progressiv", size = 4, hjust = 0.5) +
  scale_x_continuous(limits = c(-10, +110)) +
  labs(x = "Wirtschaftspolitisch",
       y = "Gesellschaftspolitisch",
       title = "Politische Positionierung im Zwei-Achsen-Modell",
       subtitle = "Durchschnitt des Abstimmungsverhaltens als Zentrum, Min-/Max als Ellipse") +
  theme_minimal()+
  theme(plot.title = element_text(size = 18,
                                  margin = margin(b = 30)),
        axis.title.x = element_text(size = 14,
                                    margin = margin(t = 5)),
        axis.title.y = element_text(size = 14,
                                    margin = margin(r = 5))) +
  coord_cartesian(ylim = c(0, 100),
                  xlim = c(0, 100),
                  clip = "off")

3.3.1.4 Hauptparteien: Durchschnittswerte als Zentrum und Min/Max als Ellipse / Verbände sowie Klein- und Regionalparteien nur mit ihrem Durchschnittswert

Code anzeigen
ggplot(parties_rating,
       aes(x = eco_pct_avg_x,
           y = socio_pct_avg_y,
           label = Partei)) +
  geom_ellipse(
    aes(x0 = eco_pct_avg_x,
        y0 = socio_pct_avg_y,
        a = (eco_pct_max_x - eco_pct_min_x) / 2, # Halbachse x
        b = (socio_pct_max_y - socio_pct_min_y) / 2, # Halbachse y
        angle = 0),
    fill = "gray80", alpha = 0.4) +
  geom_point(size = 3) +
  
  # Kleinere Parteien
  geom_point(data = subset(parties_rating,
                           `Grosse Partei` == "n" #&
                           #Relevanz_nationale_Abstimmungen != "-"
                           )
                  ,
    aes(x = eco_x, y = socio_y),
    shape = 21, fill = "grey", color = "black", size = 1, stroke = 1
  ) +
  # Labels der kleineren Partein
  geom_text(data = subset(parties_rating,
                          `Grosse Partei` == "n" #&
                          #Relevanz_nationale_Abstimmungen != "-"
                  )
                  ,
    aes(x = eco_x, y = socio_y, label = Partei),
    vjust = -1, fontface = "plain", color = "darkgrey"
  ) +

  geom_text(vjust = -0.8) +
  geom_segment(aes(x = 50, xend = 50,
                   y = 0, yend = 100), linetype = "dashed", color = "blue") +
  geom_segment(aes(x = 0, xend = 100,
                   y = 50, yend = 50), linetype = "dashed", color = "blue") +
  annotate("text",
           x = 0,
           y = min(parties_rating$socio_pct_min_y) + 55,
           label = "Links/staatsgläubig", size = 4, hjust = 0.5) +
  annotate("text",
           x = 100,
           y = min(parties_rating$socio_pct_min_y) + 55,
           label = "Rechts/marktwirtschaftlich", size = 4, hjust = 0.5) +
  annotate("text",
           x = 50,
           y = min(parties_rating$socio_pct_min_y) - 3,
           label = "Autoritär/Konservativ", size = 4, hjust = 0.5) +
  annotate("text",
           x = 50,
           y = min(parties_rating$socio_pct_min_y) + 103,
           label = "Libertär/Progressiv", size = 4, hjust = 0.5) +
  scale_x_continuous(limits = c(-10, +110)) +
  labs(x = "Wirtschaftspolitisch",
       y = "Gesellschaftspolitisch",
       title = "Politische Positionierung im Zwei-Achsen-Modell",
       subtitle = "Grundlage: Durchschnitt des Abstimmungsverhaltens als Zentrum, Min-/Max als Ellipse") +
  theme_minimal()+
  theme(plot.title = element_text(size = 18,
                                  margin = margin(b = 30)),
        axis.title.x = element_text(size = 14,
                                    margin = margin(t = 5)),
        axis.title.y = element_text(size = 14,
                                    margin = margin(r = 5))) +
  coord_cartesian(ylim = c(0, 100),
                  xlim = c(0, 100),
                  clip = "off")

3.3.2 3D-Koordinatensystem (Testweise)

Code anzeigen
#install.packages("plotly")

library(plotly)

# Datenframe erstellen
parties <- data.frame(
  Partei = c("SP", "SVP", "FDP", "GPS", "GLP", "Mitte", "EVP", "EDU"),
  Wirtschaft = c(20, 85, 80, 30, 60, 60, 40, 90),      # Links = niedrig, Rechts = hoch
  Gesellschaft = c(90, 20, 55, 90, 75, 45, 50, 20),    # Libertär = hoch, Autoritär = niedrig
  Kosmopolitismus = c(90, 15, 60, 95, 85, 50, 55, 20)  # Kosmopolitisch = hoch, Nationalistisch = niedrig
)

# Interaktives 3D-Scatterplot mit Plotly
fig <- plot_ly(
  data = parties,
  x = ~Wirtschaft,
  y = ~Gesellschaft,
  z = ~Kosmopolitismus,
  text = ~Partei,
  type = 'scatter3d',
  mode = 'markers+text',
  marker = list(size = 5),
  textposition = 'top center'
)

# Achsen benennen und Layout anpassen
fig <- fig %>% layout(
  scene = list(
    xaxis = list(title = "Wirtschaftspolitik (links – rechts)"),
    yaxis = list(title = "Gesellschaftspolitik (autoritär – libertär)"),
    zaxis = list(title = "Kosmopolitismus – Nationalismus")
  ),
  title = "Schweizer Parteien im 3D-Koordinatensystem nach Kitschelt"
)

# Plot anzeigen
fig